library(openintro)

library(plotly)

# holds all variables and functions responsible for loading and saving datasets
source("hospitalization_vaccination_data_loading.R")

Project Description

About

This is a study of vaccination rate and hospital bed usage in the United States inspired by the Washington Post article Mapping America’s hospitalization and vaccination divide. In this project we will be recreating the USA map found in the article and adding interactivity so different dates and variables can be selected.

Map By Zach Levitt and Dan Keating:

Washington Post Bivariate Choropleth Map

Methods

Vaccination data is provided at the county level by the CDC and hospital bed usage data is provided by HealthData.gov. These variables are visualized with a bivariate choropleth map of Hospital Referral Regions in the United States.

Vaccination Rate is defined by county and HRRs are defined by zip code. We can’t use county data to calculate the vaccination rate of an HRR because these regions overlap. Zip codes in one HRR can live in different counties, and Zip codes in different counties can live in the same HRR.

We need to know both the population of each HRR and the vaccination rate of each part of that population.

We determine the vaccination rate of the HHRs by averaging the vaccination rate (given by county) of the zip codes in that HRR. The individual zip code’s vaccination rate needs to be weighted by that zip code’s population. Population data is obtained from the United States Census Bureau, which is unfortunately not counted by zip code, but by blocks that make up the congressional districts. To estimate the population of zip codes, we will use the 2010 census zip code tabulation records, which approximate the zip codes in which the congressional district blocks lay.

To find all zip codes in an HRR we will use a Zip Code to HRR crosswalk.

Data Sources

Summery

  1. Hospital Bed Usage in USA - per hospital
  2. Vaccination Rates in USA - per county
  3. Population Census in USA - per zcta
  4. HRR Geography in USA - per HRR number
  5. County Geography in USA - per county
  6. Crosswalk for Zip Code and HRR number

Anytime a new file is downloaded, that file is cached in the “cached-data” folder. Anytime a request is made for a dataset by date, this folder is checked first.

Local Data Caching

All data sources are downloaded from the internet. Only the needed portions of the datasets are requested from the corresponding endpoints. Before downloading, this application first checks if the dataset has already been downloaded in a local cache file. Whenever a new portion of a dataset is downloaded, it is saved to the cache folder local to the application folder.

Vaccination Rates Data per US County

Vaccination rates are obtained from the CDC: https://data.cdc.gov/Vaccinations/COVID-19-Vaccinations-in-the-United-States-County/8xkx-amqh. This dataset is large, so instead of downloading the whole dataset, this application accesses it through the SODA API and retrieves only the relevant rows and columns.

The “COVID-19 Vaccinations in the United States,County” data provides counts and percentages of people who have been vaccinated in each county of the United States.

The variables retrieved are:

  • fips
  • series_complete_pop_pct: “Percent of people who have completed a primary series (have second dose of a two-dose vaccine or one dose of a single-dose vaccine) based on the jurisdiction and county where vaccine recipient lives.”
  • administered_dose1_pop_pct: “Percent of Total Pop with at least one Dose by State of Residence”
  • booster_doses_vax_pct: “Percent of people who completed a primary series and have received a booster (or additional) dose.”

Hospital Capacity Data of USA per Hospital

Hospital bed usage counts is obtained from HealthData.gov: https://healthdata.gov/Hospital/COVID-19-Reported-Patient-Impact-and-Hospital-Capa/anag-cw7u. This dataset is large, so instead of downloading the whole dataset, this application accesses it through the SODA API and retrieves only the relevant rows and columns.

The “COVID-19 Reported Patient Impact and Hospital Capacity by Facility” data provides counts on hospital bed utilization that is aggregated weekly.

The variables retrieved are:

  • Hospital_name and fips_code
  • inpatient_beds_7_day_avg: “Average number of total number of staffed inpatient beds in your hospital including all overflow, observation, and active surge/expansion beds used for inpatients (including all ICU beds) reported in the 7-day period.”
  • inpatient_beds_used_7_day_avg: “Average of total number of staffed inpatient beds that are occupied reported during the 7-day period.”
  • inpatient_beds_used_covid_7_day_avg: “Average of reported patients currently hospitalized in an inpatient bed who have suspected or confirmed COVID-19 reported during the 7-day period.”

Inpatient bed counts are used instead of total bed counts because many hospitals that only have inpatient bed data do not include data for inpatient and outpatient totals.

Calculate ratios for hospital bed usage

We also calculate:

  • bed_usage_ratio: The ratio of hospital beds used out of 100 beds (inpatient)
  • covid_bed_usage_ratio: The ratio of hospital beds used by covid patients out of 100 beds (inpatient)
  • covid_bed_usage_total_bed_usage_ratio: The ratio of hospital beds used by covid patients out of all used beds (inpatient)

Calculate average ratios per region

The hospital bed dataset contains records for each hospital. For mapping, each map region will need to represent the average of all hospitals present in that region.

Geographic Shape Data

US County Shape Data

County shape data is obtained from the R package Albersusa.

Hospital Referral Region Shape Data

Shape data for USA HRR regions are downloaded from arcgis.com using their “FeatureServer” REST api.

https://www.arcgis.com/home/item.html?id=46bf6790c4e0455e9379ee9769b1a5ab

Crosswalk Data

HRR number to zip code translation (2019)

This crosswalk is obtained from Dartmouth Atlas as a zip file.

https://data.dartmouthatlas.org/supplemental/#crosswalks

Load Required Data

Get Geographic, Census, and Crosswalk Data

Vaccination data and hospital bed usage data are loaded during the call to the graphing function so that only the date needed is loaded into memory.

# Setup the data caching folder
create_data_folder_if_dne()
## [1] "Founds cached data folder."
# get county shapes for mapping
us_county_shape_data <- counties_sf("laea") %>% 
  mutate(fips = as.numeric(as.character(fips)))
## old-style crs object detected; please recreate object with a recent sf::st_crs()
# get state shapes for mapping
us_state_shape_data <- usa_sf("laea")
## old-style crs object detected; please recreate object with a recent sf::st_crs()
# Get hrr shapes for mapping
hrr_shape_data <- get_hrr_shapes_arcgis()
## Reading layer `hhr_shapes_arcgis_data' from data source 
##   `C:\Users\Cinde\source\Intro to Data Science\Covid_Hospitalization_and_Vaccination\cached-data\hhr_shapes_arcgis_data.shape' 
##   using driver `GeoJSON'
## Simple feature collection with 304 features and 8 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -124.7319 ymin: 24.84407 xmax: -66.95047 ymax: 49.38309
## Geodetic CRS:  WGS 84
## [1] "HRR shapes data loaded from local cache."
# Get Required Crosswalks
zip_hrr_crosswalk_data <- get_hrr_zip_crosswalk()
## [1] "Loaded zip hrr crosswalk data from local cache."
# get population census data
zcta_population_data <- get_zcta_population_data()
## [1] "Loaded ZCTA codes and population data from local cache."
hrr_to_state <- hrr_shape_data %>% 
  st_drop_geometry() %>% 
  select(HRRNUM, HRRSTATE_long)

Calculate HRR population

A dataset is needed that tracks the population percentages of each ZCTA in each county for the function that calculates vaccination rates of HRRs

calculate_hrr_population_zip_slices <- function(){
 # get population totals of hrr
  hrr_population_zip_slice = zip_hrr_crosswalk_data %>% 
    left_join(
      zcta_population_data, 
      by = c("zipcode19" = "ZCTA5")) %>%
    group_by(hrrnum) %>% 
    summarise(population = sum(POPPT, na.rm = T), zip_count = n())
  
  ## check how well the join worked ##
  
  total_zcta_pop = sum(zcta_population_data$POPPT, na.rm = T)
  us_territories_pop_wiki = 3623895
  usa_zcta_pop = total_zcta_pop - us_territories_pop_wiki
  
  total_hrr_pop = sum(hrr_population_zip_slice
                    $population, na.rm = T)
  
  percent_retained = format(total_hrr_pop / total_zcta_pop * 100, digits = 4) 
  number_lost = format(total_zcta_pop - total_hrr_pop, big.mark=",")
  
  print(paste(
    "After joining US state zip codes with ZCTAs, We keep about", 
    percent_retained, 
    "% of the population, loosing", 
    number_lost, "people." ))
  
  print(paste(
    "zctas also account for the population in us territories, which have a total population of", 
    format(us_territories_pop_wiki, big.mark = ","), "according to wikipedia."))
  print(paste(
    "This pushes the retained popoulation of the states closer to",
    format(total_hrr_pop / usa_zcta_pop * 100, digits = 4), "percent" ))
  
  return(hrr_population_zip_slice)
}

 
hrr_population_zip_slice <- calculate_hrr_population_zip_slices()
## [1] "After joining US state zip codes with ZCTAs, We keep about 98.8 % of the population, loosing 3,738,434 people."
## [1] "zctas also account for the population in us territories, which have a total population of 3,623,895 according to wikipedia."
## [1] "This pushes the retained popoulation of the states closer to 99.96 percent"

Calculate HHR vaccination rate

We are using population data for zip code tabulation areas because we don’t have counts for zip codes. ZCTAs will be our proxy for zip codes. Because zcta’s don’t line up exactly with zip codes, the same zcta can be part of more than one county. That is why it is important to know how much of the ZCTA’s population is in each county.

The Process:

  1. Join the vaccination data for 2021/09/24 with population zip code data by county.
  • Now we know the vaccination rate of each zip code
  1. Join with the zip hhr crosswalk by zip code to zcta.
  • Now we know the hrr each zip code is in along with that zip code’s vaccination rate
  1. Join with the known population counts of zip codes by zip code.
  • Now we know the slice of population of a zip code inside each county along with that population slice’s vaccination rate
  1. Calculate number of vaccinated people in each zip code’s population slice inside each county for each hrr
  2. Divide the number of vaccinated in zip slice by the total hrr population that zip code is in.
  • This gives us the “slice vaccination percentage” that contributes to the vaccination percentage of the hrr
  1. Add up all of the sliced vaccination percentages for each hrr.
calculate_hrr_vaccination_rates <- function(date) {

  vaccination_data = get_vaccination_rates_data(date)
  
  zip_hrr_vaccination_population_join = vaccination_data %>% 
    inner_join(zcta_population_data, by = c("fips" = "GEOID")) %>% 
    inner_join(zip_hrr_crosswalk_data, by = c("ZCTA5" = "zipcode19")) %>% 
    select(ZCTA5, fips, POPPT, series_complete_pop_pct, administered_dose1_pop_pct, hrrnum) %>% 
    
    rename(
      zcta = ZCTA5, 
      zip_pop_slice_in_fips = POPPT, 
      fips_vaccination_percent = series_complete_pop_pct,
      fips_one_dose_percent = administered_dose1_pop_pct) %>% 
    
    inner_join(hrr_population_zip_slice, by = "hrrnum") %>% 
    rename(total_hrr_population = population)
  
  hrr_vacc_percent = zip_hrr_vaccination_population_join %>% 
    mutate(
      num_vacc_in_zip_slice = fips_vaccination_percent * zip_pop_slice_in_fips,           # calculate number in zip of fully vaccinated
      num_one_dose_in_zip_slice = fips_one_dose_percent * zip_pop_slice_in_fips) %>%       # calculate number in zip of one dose
    mutate(
      zip_slice_weighted_vacc_percent = num_vacc_in_zip_slice / total_hrr_population,                  # calculate weighted portion of fully vaccinated
      zip_slice_weighted_single_dose_percent = num_one_dose_in_zip_slice / total_hrr_population) %>%   # calculate weighted portion of single dose
    select(hrrnum, zip_slice_weighted_vacc_percent, zip_slice_weighted_single_dose_percent) %>% 
    group_by(hrrnum) %>% 
    summarise(
      vacc_complete_percent = sum(zip_slice_weighted_vacc_percent),
      single_dose_percent = sum(zip_slice_weighted_single_dose_percent))
}

Graph by Date Functions

These functions retrieve data for vaccination rates and hospital bed usage from their respective APIs

possible_axis_labels <- c(
  "vacc_complete_percent"                 = "Population Fully Vaccinated",
  "single_dose_percent"                   = "Population With Single Dose",
  "bed_usage_ratio"                       = "Hospital Beds Used",
  "covid_bed_usage_ratio"                 = "Hospital Beds Used for Covid",
  "covid_bed_usage_total_bed_usage_ratio" = "Covid Bed to Total Bed Usage")


### Method for selecting appropriate dates for data

valid_vaccination_dates <- read_csv("valid_vaccination_dates.csv", show_col_types = F)$Date
valid_bed_usage_dates <- read_csv("valid_bed_usage_dates.csv", show_col_types = F)$collection_week

closest_valid_dates <- function(date, list) {
  
  closest_index = which.min(abs(ymd(date) - valid_vaccination_dates))
  vac_date = valid_vaccination_dates[closest_index]
  
  closest_index = which.min(abs(ymd(date) - valid_bed_usage_dates))
  bed_date = valid_bed_usage_dates[closest_index]
  
  return(c(vac_date, bed_date))
  
}

graph_interactive <- function(graph){
  ggplotly(graph, tooltip = "text") %>% 
    style(hoveron = "fill") 
}

Point Plot of Vaccination and Hospitalization Stats

### Vaccination Rate by HHR Graphing Function

### Stat Selectable Interactive Graph of Vaccination Rates by HRR
#
# Values for `x_axis` and `y_axis`:
#         vacc_complete_percent: Percentage of people fully vaccinated in that HRR
#         single_dose_percent:   Percentage of people with one vaccine dose in that HRR
#         bed_usage_ratio:                         Percentage of hospital beds used in HRR
#         covid_bed_usage_ratio:                   Percentage of beds used for COVID in HRR
#         covid_bed_usage_total_bed_usage_ratio:   Ratio of COVID bed usage to total bed usage
#
# Date must be given in yyyymmdd format
Generate_Vaccination_Plot <- function(date, x_axis, y_axis){
  
  x_axis_stat = rlang::parse_expr(x_axis)
  y_axis_stat = rlang::parse_expr(y_axis)

  x_axis_label = paste0(possible_axis_labels[x_axis])
  y_axis_label = paste0(possible_axis_labels[y_axis])  

  dates = closest_valid_dates(ymd(date))
  
  # Get Vaccination Data
  vaccination_data_per_hrr = calculate_hrr_vaccination_rates(dates[1])
    
  ## Get Hospital Data
  hospital_bed_data_per_hospital = get_bed_utilization_data(dates[2]) %>%  # don't include hospitals with 0 beds
    filter(inpatient_beds_7_day_avg > 1, state != "TX")                    # TX doesn't have vaccination data
  
  # calculate bed usage ratios
  hospital_bed_data_per_hospital = hospital_bed_data_per_hospital %>% 
    mutate(
      bed_usage_ratio = inpatient_beds_used_7_day_avg / inpatient_beds_7_day_avg * 100,
      covid_bed_usage_ratio = inpatient_beds_used_covid_7_day_avg / inpatient_beds_7_day_avg * 100,
      covid_bed_usage_total_bed_usage_ratio = inpatient_beds_used_covid_7_day_avg / inpatient_beds_used_7_day_avg
    )
  
  # Group Hospitals in HRR
  bed_ratios_per_hrr = hospital_bed_data_per_hospital %>% 
    left_join(zip_hrr_crosswalk_data, by = c("zip" = "zipcode19")) %>% 
    group_by(hrrnum) %>% 
    summarise(
      bed_usage_ratio = mean(bed_usage_ratio, na.rm = TRUE), 
      covid_bed_usage_ratio = mean(covid_bed_usage_ratio, na.rm = TRUE), 
      covid_bed_usage_total_bed_usage_ratio = mean(covid_bed_usage_total_bed_usage_ratio, na.rm = TRUE))
    
  # Combine Vaccination and Bed Data (and hrr/state crosswalk)
  bed_utilization_vaccination_data_hrr = bed_ratios_per_hrr %>% 
    left_join(vaccination_data_per_hrr, by = "hrrnum") %>% 
    left_join(hrr_population_zip_slice, by = "hrrnum") %>% 
    left_join(hrr_to_state, by = c("hrrnum" = "HRRNUM"))
  
  plot_data = bed_utilization_vaccination_data_hrr %>% 
    drop_na() %>%                                         #drop unplottable rows
    
    mutate(text = paste0(
      "HRR #: ", hrrnum,
      "</b>\nState: ", HRRSTATE_long, 
      "</b>\nFully Vaccinated: ", format(vacc_complete_percent, digits = 2), "%",
      "</b>\nHad Single Dose: ", format(single_dose_percent, digits = 1), "%",
      "</b>\nZip Code Count: ", zip_count,
      "</b>\nHRR Pop: ", format(population, big.mark = ","))) %>% 
    mutate(vacc_complete_percent = vacc_complete_percent / 100,
           single_dose_percent = single_dose_percent/100,
           covid_bed_usage_ratio = covid_bed_usage_ratio/100,
           bed_usage_ratio = bed_usage_ratio/100) %>% 
    
    ggplot(aes(x = !!x_axis_stat, y = !!y_axis_stat)) +
      geom_point(color = "black", size = 1.4) +
      geom_point(aes(color = population, text=text), size = 1.2) +
      scale_color_continuous(
          "HRR Population", 
          trans = "log10",
          type = "gradient", 
          labels = scales::comma,
          low = "blue",
          high = "gold") +
    geom_smooth(method = lm, se = F, linewidth = 0.5) +
    scale_x_continuous(x_axis_label, labels=scales::percent) +
    scale_y_continuous(y_axis_label, labels=scales::percent)
}

`

Map By Date Graphs

graph_interactive <- function(graph){
  ggplotly(graph, tooltip = "text") %>% 
    style(hoveron = "fills") 
}

##### VACCINATION RATE

#####
# Date must be given in yyyymmdd format
Graph_Vaccination_Rates_By_County <- function(date) {
  vaccination_data = get_vaccination_rates_data(date = date)
  
  vaccination_data = us_county_shape_data %>% 
    left_join(vaccination_data, by = "fips") 
    
  vaccination_data %>% 
    ggplot() +
    geom_sf(aes(fill = series_complete_pop_pct/100)) +
    scale_fill_continuous("Fully Vaccinated", low="red", high="yellow", labels = scales::percent) +
    ggtitle("COVID Vaccination Status", subtitle = "Percentage of county population that is fully vaccinated (from CDC)") +
    my_map_theme()
}

# Date must be given in yyyymmdd format
Graph_Vaccination_Rates_By_Hrr <- function(date) {
  vaccination_data <- calculate_hrr_vaccination_rates(date)
  
  hrr_ggplot_data = hrr_shape_data %>% 
    left_join(vaccination_data, by = c("HRRNUM" = "hrrnum")) %>% 
    select(!HRR)  %>% 
    st_transform(crs= "EPSG:2163") %>% 
      mutate(text = paste0(
      "State: ", HRRSTATE_long, 
      "</b>\nFully Vaccinated: ", format(vacc_complete_percent, digits = 4), "%",
      "</b>\nHad Single Dose: ", format(single_dose_percent, digits = 4), "%",
      "</b>\nHRR #: ", HRRNUM,
      "</b>\nZip Code Count: ", hrr_population_zip_slice$zip_count[HRRNUM],
      "</b>\nHRR Pop: ", hrr_population_zip_slice$population[HRRNUM]))
  
  hrr_ggplot_data %>% 
    ggplot() +
      geom_sf(
        aes(fill = vacc_complete_percent/100 + runif(nrow(hrr_ggplot_data), min=0, max=0.001), 
            text=text), 
        linewidth = 0.1, 
        color=alpha("black",0.5)) +
      # geom_sf(data = us_state_shape_data, fill = alpha("black", 0.0)) +
      scale_fill_continuous("Fully Vaccinated", low="red", high="yellow", labels = scales::percent) +
      ggtitle("COVID Vaccination Status", subtitle = "Percentage of county population that is fully vaccinated (from CDC)") +
      my_map_theme()
}

##### HOSPITAL BED USAGE

#####
# Date must be given in yyyymmdd format
Graph_Hospital_Bed_Usage_By_County <- function(date){
  hospital_bed_data = get_bed_utilization_data(date = date)
  
  # calculate ratios
  hospital_bed_data = hospital_bed_data %>% 
    mutate(
      bed_usage_ratio = inpatient_beds_used_7_day_avg / inpatient_beds_7_day_avg * 100,
      covid_bed_usage_ratio = inpatient_beds_used_covid_7_day_avg / inpatient_beds_7_day_avg * 100,
      covid_bed_usage_total_bed_usage_ratio = inpatient_beds_used_covid_7_day_avg / inpatient_beds_used_7_day_avg
    )
  
  
  county_bed_ratios = hospital_bed_data %>% 
    group_by(fips_code) %>% 
    summarise(
      bed_usage_ratio = mean(bed_usage_ratio, na.rm = TRUE), 
      covid_bed_usage_ratio = mean(covid_bed_usage_ratio, na.rm = TRUE), 
      covid_bed_usage_total_bed_usage_ratio = mean(covid_bed_usage_total_bed_usage_ratio, na.rm = TRUE))
  
  
  
  us_county_shape_data %>% 
    left_join(county_bed_ratios, by = c("fips" = "fips_code")) %>% 
    
    ggplot() +
      geom_sf(aes(fill = covid_bed_usage_ratio/100)) +
      scale_fill_continuous("Beds Used for Covid", low="deepskyblue", high="green", labels = scales::percent) +
      ggtitle("Hospital Bed Usage", subtitle = "Percentage of county's total hospital beds used by covid patients") +
      my_map_theme()
}

#####
# Date must be given in yyyymmdd format
Graph_Hospital_Bed_Usage_By_HRR <- function(date){
  hospital_bed_data = get_bed_utilization_data(date)
  
# calculate ratios
  hospital_bed_data = hospital_bed_data %>% 
    mutate(
      bed_usage_ratio = inpatient_beds_used_7_day_avg / inpatient_beds_7_day_avg * 100,
      covid_bed_usage_ratio = inpatient_beds_used_covid_7_day_avg / inpatient_beds_7_day_avg * 100,
      covid_bed_usage_total_bed_usage_ratio = inpatient_beds_used_covid_7_day_avg / inpatient_beds_used_7_day_avg
    )
  
  hrr_bed_ratios = hospital_bed_data %>% 
    left_join(zip_hrr_crosswalk_data, by = c("zip" = "zipcode19")) %>% 
    group_by(hrrnum) %>% 
    summarise(
      bed_usage_ratio = mean(bed_usage_ratio, na.rm = TRUE), 
      covid_bed_usage_ratio = mean(covid_bed_usage_ratio, na.rm = TRUE), 
      covid_bed_usage_total_bed_usage_ratio = mean(covid_bed_usage_total_bed_usage_ratio, na.rm = TRUE))
  
  hrr_ggplot_data = hrr_shape_data %>% 
    left_join(hrr_bed_ratios, by = c("HRRNUM" = "hrrnum")) %>% 
    select(!HRR)  %>% 
    st_transform(crs= "EPSG:2163")
  
  hrr_ggplot_data %>% 
    ggplot() +
      geom_sf(aes(fill = covid_bed_usage_ratio/100), linewidth = 0, color=alpha("black",0.02)) +
      geom_sf(data = us_state_shape_data, fill = alpha("black", 0.0)) +
      scale_fill_continuous("Beds Used for Covid", low="purple", high="orange", labels = scales::percent) +
      ggtitle("Hospital Bed Usage", subtitle = "Percentage of hospital referral region's beds used by covid patients") +
      my_map_theme()
}

Example Graphs

Vaccination and Hospitalization Point Plot

date = "2021-09-23"

plot_data = Generate_Vaccination_Plot(date, "vacc_complete_percent", "covid_bed_usage_ratio")
## [1] "Vaccination records loaded from local cache."
## [1] "Hospital bed data loaded from local cache."
## Warning in geom_point(aes(color = population, text = text), size = 1.2):
## Ignoring unknown aesthetics: text
graph_interactive(plot_data)
## `geom_smooth()` using formula = 'y ~ x'
plot_data = Generate_Vaccination_Plot(date, "covid_bed_usage_ratio", "vacc_complete_percent")
## [1] "Vaccination records loaded from local cache."
## [1] "Hospital bed data loaded from local cache."
## Warning in geom_point(aes(color = population, text = text), size = 1.2):
## Ignoring unknown aesthetics: text
graph_interactive(plot_data)
## `geom_smooth()` using formula = 'y ~ x'
plot_data = Generate_Vaccination_Plot(date, "single_dose_percent", "covid_bed_usage_total_bed_usage_ratio")
## [1] "Vaccination records loaded from local cache."
## [1] "Hospital bed data loaded from local cache."
## Warning in geom_point(aes(color = population, text = text), size = 1.2):
## Ignoring unknown aesthetics: text
graph_interactive(plot_data)
## `geom_smooth()` using formula = 'y ~ x'

Vaccination Rates Map

By County

Graph_Vaccination_Rates_By_County("2021/03/1")
## [1] "Vaccination records loaded from local cache."

Graph_Vaccination_Rates_By_County("2021/09/24")
## [1] "Vaccination records loaded from local cache."

By HRR

graph_interactive(Graph_Vaccination_Rates_By_Hrr("2021/09/24"))
## [1] "Vaccination records loaded from local cache."
## Warning in layer_sf(geom = GeomSf, data = data, mapping = mapping, stat =
## stat, : Ignoring unknown aesthetics: text

Hospital Bed Usage Map

By County

Graph_Hospital_Bed_Usage_By_County("2020/11/06")
## [1] "Hospital bed data loaded from local cache."

Graph_Hospital_Bed_Usage_By_County("2022/07/22")
## [1] "Hospital bed data loaded from local cache."

By HRR

Graph_Hospital_Bed_Usage_By_HRR("2020/11/06")
## [1] "Hospital bed data loaded from local cache."

Graph_Hospital_Bed_Usage_By_HRR("2022/07/22")
## [1] "Hospital bed data loaded from local cache."

Selectable Stat Map

### Vaccination Rate by HHR Graphing Function

### Stat Selectable Interactive Graph of Vaccination Rates by HRR
# Values for `display_stat`:
#         vacc_complete_percent: Percentage of people fully vaccinated in that HRR
#         single_dose_percent:   Percentage of people with one vaccine dose in that HRR
# Date must be given in yyyymmdd format
Graph_Vaccination_Rates_By_Hrr <- function(date, display_stat) {
  
  graph_stat = enquo(display_stat)
  
  vaccination_data <- calculate_hrr_vaccination_rates(date)
  
  hrr_ggplot_data = hrr_shape_data %>% 
    left_join(vaccination_data, by = c("HRRNUM" = "hrrnum")) %>% 
    select(!HRR)  %>% 
    st_transform(crs= "EPSG:2163") %>% 
    mutate(text = paste0(
      "HRR #: ", HRRNUM,
      "</b>\nState: ", HRRSTATE_long, 
      "</b>\nFully Vaccinated: ", format(vacc_complete_percent, digits = 4), "%",
      "</b>\nHad Single Dose: ", format(single_dose_percent, digits = 4), "%",
      "</b>\nZip Code Count: ", hrr_population_zip_slice$zip_count[HRRNUM],
      "</b>\nHRR Pop: ", hrr_population_zip_slice$population[HRRNUM]))
  
  hrr_ggplot_data %>% 
    ggplot() +
    geom_sf(
      aes(fill = !!graph_stat/100 + runif(nrow(hrr_ggplot_data), min=0, max=0.001), 
          text=text), 
      linewidth = 0.1, 
      color=alpha("black",0.5)) +
    scale_fill_continuous(
      "HRR Population", 
      type = "viridis", 
      labels = scales::percent, breaks = c(0, .2, .40, .60, .8, 1),  
      limits= c( 0, 1)) +
    my_map_theme()
}

graph_interactive(Graph_Vaccination_Rates_By_Hrr("2021/09/24", vacc_complete_percent))
## [1] "Vaccination records loaded from local cache."
## Warning in layer_sf(geom = GeomSf, data = data, mapping = mapping, stat =
## stat, : Ignoring unknown aesthetics: text

LS0tDQp0aXRsZTogIkNvdmlkIEhvc3BpdGFsaXphdGlvbiBhbmQgVmFjY2luYXRpb24gU3R1ZHkiDQphdXRob3I6ICJHcmVnb3J5IE1heW5hcmQiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydA0KLS0tDQoNCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KG9wZW5pbnRybykNCg0KbGlicmFyeShwbG90bHkpDQoNCiMgaG9sZHMgYWxsIHZhcmlhYmxlcyBhbmQgZnVuY3Rpb25zIHJlc3BvbnNpYmxlIGZvciBsb2FkaW5nIGFuZCBzYXZpbmcgZGF0YXNldHMNCnNvdXJjZSgiaG9zcGl0YWxpemF0aW9uX3ZhY2NpbmF0aW9uX2RhdGFfbG9hZGluZy5SIikNCmBgYA0KDQojIFByb2plY3QgRGVzY3JpcHRpb24NCg0KIyMgQWJvdXQNCg0KVGhpcyBpcyBhIHN0dWR5IG9mIHZhY2NpbmF0aW9uIHJhdGUgYW5kIGhvc3BpdGFsIGJlZCB1c2FnZSBpbiB0aGUgVW5pdGVkIFN0YXRlcyBpbnNwaXJlZCBieSB0aGUgV2FzaGluZ3RvbiBQb3N0IGFydGljbGUgW01hcHBpbmcgQW1lcmljYeKAmXMgaG9zcGl0YWxpemF0aW9uIGFuZCB2YWNjaW5hdGlvbiBkaXZpZGVdKGh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9oZWFsdGgvMjAyMS8wOS8yMy9jb3ZpZC12YWNjaW5hdGlvbi1ob3NwaXRhbGl6YXRpb24tbWFwLykuIEluIHRoaXMgcHJvamVjdCB3ZSB3aWxsIGJlIHJlY3JlYXRpbmcgdGhlIFVTQSBtYXAgZm91bmQgaW4gdGhlIGFydGljbGUgYW5kIGFkZGluZyBpbnRlcmFjdGl2aXR5IHNvIGRpZmZlcmVudCBkYXRlcyBhbmQgdmFyaWFibGVzIGNhbiBiZSBzZWxlY3RlZC4NCg0KIyMjIyAqKk1hcCBCeSBaYWNoIExldml0dCBhbmQgRGFuIEtlYXRpbmc6KioNCg0KIVtXYXNoaW5ndG9uIFBvc3QgQml2YXJpYXRlIENob3JvcGxldGggTWFwXSh3YXNoaW5ndG9uLXBvc3QtbWFwLnBuZykNCg0KIyMgTWV0aG9kcw0KDQpWYWNjaW5hdGlvbiBkYXRhIGlzIHByb3ZpZGVkIGF0IHRoZSBjb3VudHkgbGV2ZWwgYnkgdGhlIENEQyBhbmQgaG9zcGl0YWwgYmVkIHVzYWdlIGRhdGEgaXMgcHJvdmlkZWQgYnkgSGVhbHRoRGF0YS5nb3YuIFRoZXNlIHZhcmlhYmxlcyBhcmUgdmlzdWFsaXplZCB3aXRoIGEgYml2YXJpYXRlIGNob3JvcGxldGggbWFwIG9mIEhvc3BpdGFsIFJlZmVycmFsIFJlZ2lvbnMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMuDQoNClZhY2NpbmF0aW9uIFJhdGUgaXMgZGVmaW5lZCBieSBjb3VudHkgYW5kIEhSUnMgYXJlIGRlZmluZWQgYnkgemlwIGNvZGUuIFdlIGNhbid0IHVzZSBjb3VudHkgZGF0YSB0byBjYWxjdWxhdGUgdGhlIHZhY2NpbmF0aW9uIHJhdGUgb2YgYW4gSFJSIGJlY2F1c2UgdGhlc2UgcmVnaW9ucyBvdmVybGFwLiBaaXAgY29kZXMgaW4gb25lIEhSUiBjYW4gbGl2ZSBpbiBkaWZmZXJlbnQgY291bnRpZXMsIGFuZCBaaXAgY29kZXMgaW4gZGlmZmVyZW50IGNvdW50aWVzIGNhbiBsaXZlIGluIHRoZSBzYW1lIEhSUi4NCg0KKipXZSBuZWVkIHRvIGtub3cgYm90aCB0aGUgcG9wdWxhdGlvbiBvZiBlYWNoIEhSUiBhbmQgdGhlIHZhY2NpbmF0aW9uIHJhdGUgb2YgZWFjaCBwYXJ0IG9mIHRoYXQgcG9wdWxhdGlvbi4qKg0KDQpXZSBkZXRlcm1pbmUgdGhlIHZhY2NpbmF0aW9uIHJhdGUgb2YgdGhlIEhIUnMgYnkgYXZlcmFnaW5nIHRoZSB2YWNjaW5hdGlvbiByYXRlIChnaXZlbiBieSBjb3VudHkpIG9mIHRoZSB6aXAgY29kZXMgaW4gdGhhdCBIUlIuIFRoZSBpbmRpdmlkdWFsIHppcCBjb2RlJ3MgdmFjY2luYXRpb24gcmF0ZSBuZWVkcyB0byBiZSB3ZWlnaHRlZCBieSB0aGF0IHppcCBjb2RlJ3MgcG9wdWxhdGlvbi4gUG9wdWxhdGlvbiBkYXRhIGlzIG9idGFpbmVkIGZyb20gdGhlIFVuaXRlZCBTdGF0ZXMgQ2Vuc3VzIEJ1cmVhdSwgd2hpY2ggaXMgdW5mb3J0dW5hdGVseSBub3QgY291bnRlZCBieSB6aXAgY29kZSwgYnV0IGJ5IGJsb2NrcyB0aGF0IG1ha2UgdXAgdGhlIGNvbmdyZXNzaW9uYWwgZGlzdHJpY3RzLiBUbyBlc3RpbWF0ZSB0aGUgcG9wdWxhdGlvbiBvZiB6aXAgY29kZXMsIHdlIHdpbGwgdXNlIHRoZSAyMDEwIGNlbnN1cyB6aXAgY29kZSB0YWJ1bGF0aW9uIHJlY29yZHMsIHdoaWNoIGFwcHJveGltYXRlIHRoZSB6aXAgY29kZXMgaW4gd2hpY2ggdGhlIGNvbmdyZXNzaW9uYWwgZGlzdHJpY3QgYmxvY2tzIGxheS4NCg0KVG8gZmluZCBhbGwgemlwIGNvZGVzIGluIGFuIEhSUiB3ZSB3aWxsIHVzZSBhIFppcCBDb2RlIHRvIEhSUiBjcm9zc3dhbGsuDQoNCiMgRGF0YSBTb3VyY2VzDQoNCiMjIFN1bW1lcnkNCg0KMS4gSG9zcGl0YWwgQmVkIFVzYWdlIGluIFVTQSAtIHBlciBob3NwaXRhbA0KMi4gVmFjY2luYXRpb24gUmF0ZXMgaW4gVVNBIC0gcGVyIGNvdW50eQ0KMy4gUG9wdWxhdGlvbiBDZW5zdXMgaW4gVVNBIC0gcGVyIHpjdGENCjQuIEhSUiBHZW9ncmFwaHkgaW4gVVNBIC0gcGVyIEhSUiBudW1iZXINCjUuIENvdW50eSBHZW9ncmFwaHkgaW4gVVNBIC0gcGVyIGNvdW50eQ0KNi4gQ3Jvc3N3YWxrIGZvciBaaXAgQ29kZSBhbmQgSFJSIG51bWJlcg0KDQpBbnl0aW1lIGEgbmV3IGZpbGUgaXMgZG93bmxvYWRlZCwgdGhhdCBmaWxlIGlzIGNhY2hlZCBpbiB0aGUgImNhY2hlZC1kYXRhIiBmb2xkZXIuIEFueXRpbWUgYSByZXF1ZXN0IGlzIG1hZGUgDQpmb3IgYSBkYXRhc2V0IGJ5IGRhdGUsIHRoaXMgZm9sZGVyIGlzIGNoZWNrZWQgZmlyc3QuDQoNCiMjIExvY2FsIERhdGEgQ2FjaGluZw0KDQpBbGwgZGF0YSBzb3VyY2VzIGFyZSBkb3dubG9hZGVkIGZyb20gdGhlIGludGVybmV0LiBPbmx5IHRoZSBuZWVkZWQgcG9ydGlvbnMgb2YgdGhlIGRhdGFzZXRzIGFyZSByZXF1ZXN0ZWQgZnJvbSB0aGUgY29ycmVzcG9uZGluZyBlbmRwb2ludHMuIA0KQmVmb3JlIGRvd25sb2FkaW5nLCB0aGlzIGFwcGxpY2F0aW9uIGZpcnN0IGNoZWNrcyBpZiB0aGUgZGF0YXNldCBoYXMgYWxyZWFkeSBiZWVuIGRvd25sb2FkZWQgaW4gYSBsb2NhbCBjYWNoZSANCmZpbGUuIFdoZW5ldmVyIGEgbmV3IHBvcnRpb24gb2YgYSBkYXRhc2V0IGlzIGRvd25sb2FkZWQsIGl0IGlzIHNhdmVkIHRvIHRoZSBjYWNoZSBmb2xkZXIgbG9jYWwgdG8gdGhlIGFwcGxpY2F0aW9uIA0KZm9sZGVyLg0KDQojIyBWYWNjaW5hdGlvbiBSYXRlcyBEYXRhIHBlciBVUyBDb3VudHkNCg0KVmFjY2luYXRpb24gcmF0ZXMgYXJlIG9idGFpbmVkIGZyb20gdGhlIENEQzogaHR0cHM6Ly9kYXRhLmNkYy5nb3YvVmFjY2luYXRpb25zL0NPVklELTE5LVZhY2NpbmF0aW9ucy1pbi10aGUtVW5pdGVkLVN0YXRlcy1Db3VudHkvOHhreC1hbXFoLiANClRoaXMgZGF0YXNldCBpcyBsYXJnZSwgc28gaW5zdGVhZCBvZiBkb3dubG9hZGluZyB0aGUgd2hvbGUgZGF0YXNldCwgdGhpcyBhcHBsaWNhdGlvbiBhY2Nlc3NlcyBpdCB0aHJvdWdoIHRoZSBTT0RBIEFQSSBhbmQgcmV0cmlldmVzIG9ubHkgdGhlIA0KcmVsZXZhbnQgcm93cyBhbmQgY29sdW1ucy4NCg0KVGhlICJDT1ZJRC0xOSBWYWNjaW5hdGlvbnMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMsQ291bnR5IiBkYXRhIHByb3ZpZGVzIGNvdW50cyBhbmQgcGVyY2VudGFnZXMgb2YgcGVvcGxlIHdobyBoYXZlIGJlZW4gDQp2YWNjaW5hdGVkIGluIGVhY2ggY291bnR5IG9mIHRoZSBVbml0ZWQgU3RhdGVzLg0KDQpUaGUgdmFyaWFibGVzIHJldHJpZXZlZCBhcmU6DQoNCiogKipmaXBzKioNCiogKipzZXJpZXNfY29tcGxldGVfcG9wX3BjdCoqOiAiUGVyY2VudCBvZiBwZW9wbGUgd2hvIGhhdmUgY29tcGxldGVkIGEgcHJpbWFyeSBzZXJpZXMgKGhhdmUgc2Vjb25kIGRvc2Ugb2YgYSB0d28tZG9zZSB2YWNjaW5lIG9yIG9uZSBkb3NlIG9mIGEgc2luZ2xlLWRvc2UgdmFjY2luZSkgYmFzZWQgb24gdGhlIGp1cmlzZGljdGlvbiBhbmQgY291bnR5IHdoZXJlIHZhY2NpbmUgcmVjaXBpZW50IGxpdmVzLiINCiogKiphZG1pbmlzdGVyZWRfZG9zZTFfcG9wX3BjdCoqOiAiUGVyY2VudCBvZiBUb3RhbCBQb3Agd2l0aCBhdCBsZWFzdCBvbmUgRG9zZSBieSBTdGF0ZSBvZiBSZXNpZGVuY2UiDQoqICoqYm9vc3Rlcl9kb3Nlc192YXhfcGN0Kio6ICJQZXJjZW50IG9mIHBlb3BsZSB3aG8gY29tcGxldGVkIGEgcHJpbWFyeSBzZXJpZXMgYW5kIGhhdmUgcmVjZWl2ZWQgYSBib29zdGVyIChvciBhZGRpdGlvbmFsKSBkb3NlLiINCg0KIyMgSG9zcGl0YWwgQ2FwYWNpdHkgRGF0YSBvZiBVU0EgcGVyIEhvc3BpdGFsDQoNCkhvc3BpdGFsIGJlZCB1c2FnZSBjb3VudHMgaXMgb2J0YWluZWQgZnJvbSBIZWFsdGhEYXRhLmdvdjogaHR0cHM6Ly9oZWFsdGhkYXRhLmdvdi9Ib3NwaXRhbC9DT1ZJRC0xOS1SZXBvcnRlZC1QYXRpZW50LUltcGFjdC1hbmQtSG9zcGl0YWwtQ2FwYS9hbmFnLWN3N3UuIA0KVGhpcyBkYXRhc2V0IGlzIGxhcmdlLCBzbyBpbnN0ZWFkIG9mIGRvd25sb2FkaW5nIHRoZSB3aG9sZSBkYXRhc2V0LCB0aGlzIGFwcGxpY2F0aW9uIGFjY2Vzc2VzIGl0IHRocm91Z2ggdGhlIFNPREEgQVBJIGFuZCByZXRyaWV2ZXMgb25seSB0aGUgDQpyZWxldmFudCByb3dzIGFuZCBjb2x1bW5zLg0KDQpUaGUgIkNPVklELTE5IFJlcG9ydGVkIFBhdGllbnQgSW1wYWN0IGFuZCBIb3NwaXRhbCBDYXBhY2l0eSBieSBGYWNpbGl0eSIgZGF0YSBwcm92aWRlcyBjb3VudHMgb24gaG9zcGl0YWwgYmVkIHV0aWxpemF0aW9uIHRoYXQgaXMgYWdncmVnYXRlZCB3ZWVrbHkuDQoNClRoZSB2YXJpYWJsZXMgcmV0cmlldmVkIGFyZToNCg0KKiAqKkhvc3BpdGFsX25hbWUqKiBhbmQgKipmaXBzX2NvZGUqKg0KKiAqKmlucGF0aWVudF9iZWRzXzdfZGF5X2F2ZyoqOiAiQXZlcmFnZSBudW1iZXIgb2YgdG90YWwgbnVtYmVyIG9mIHN0YWZmZWQgaW5wYXRpZW50IGJlZHMgaW4geW91ciBob3NwaXRhbCBpbmNsdWRpbmcgYWxsIG92ZXJmbG93LCBvYnNlcnZhdGlvbiwgYW5kIGFjdGl2ZSBzdXJnZS9leHBhbnNpb24gYmVkcyB1c2VkIGZvciBpbnBhdGllbnRzIChpbmNsdWRpbmcgYWxsIElDVSBiZWRzKSByZXBvcnRlZCBpbiB0aGUgNy1kYXkgcGVyaW9kLiINCiogKippbnBhdGllbnRfYmVkc191c2VkXzdfZGF5X2F2ZyoqOiAiQXZlcmFnZSBvZiB0b3RhbCBudW1iZXIgb2Ygc3RhZmZlZCBpbnBhdGllbnQgYmVkcyB0aGF0IGFyZSBvY2N1cGllZCByZXBvcnRlZCBkdXJpbmcgdGhlIDctZGF5IHBlcmlvZC4iDQoqICoqaW5wYXRpZW50X2JlZHNfdXNlZF9jb3ZpZF83X2RheV9hdmcqKjogIkF2ZXJhZ2Ugb2YgcmVwb3J0ZWQgcGF0aWVudHMgY3VycmVudGx5IGhvc3BpdGFsaXplZCBpbiBhbiBpbnBhdGllbnQgYmVkIHdobyBoYXZlIHN1c3BlY3RlZCBvciBjb25maXJtZWQgQ09WSUQtMTkgcmVwb3J0ZWQgZHVyaW5nIHRoZSA3LWRheSBwZXJpb2QuIg0KDQoqSW5wYXRpZW50IGJlZCBjb3VudHMgYXJlIHVzZWQgaW5zdGVhZCBvZiB0b3RhbCBiZWQgY291bnRzIGJlY2F1c2UgbWFueSBob3NwaXRhbHMgdGhhdCBvbmx5IGhhdmUgaW5wYXRpZW50IGJlZCBkYXRhIGRvIG5vdCBpbmNsdWRlIGRhdGEgZm9yIGlucGF0aWVudCBhbmQgb3V0cGF0aWVudCB0b3RhbHMuKg0KDQoNCiMjIyMgQ2FsY3VsYXRlIHJhdGlvcyBmb3IgaG9zcGl0YWwgYmVkIHVzYWdlDQoNCldlIGFsc28gY2FsY3VsYXRlOiANCg0KKiAqKmJlZF91c2FnZV9yYXRpbyoqOiBUaGUgcmF0aW8gb2YgaG9zcGl0YWwgYmVkcyB1c2VkIG91dCBvZiAxMDAgYmVkcyAoaW5wYXRpZW50KQ0KKiAqKmNvdmlkX2JlZF91c2FnZV9yYXRpbyoqOiBUaGUgcmF0aW8gb2YgaG9zcGl0YWwgYmVkcyB1c2VkIGJ5IGNvdmlkIHBhdGllbnRzIG91dCBvZiAxMDAgYmVkcyAoaW5wYXRpZW50KQ0KKiAqKmNvdmlkX2JlZF91c2FnZV90b3RhbF9iZWRfdXNhZ2VfcmF0aW8qKjogVGhlIHJhdGlvIG9mIGhvc3BpdGFsIGJlZHMgdXNlZCBieSBjb3ZpZCBwYXRpZW50cyBvdXQgb2YgYWxsIHVzZWQgYmVkcyAoaW5wYXRpZW50KQ0KDQojIyMjIENhbGN1bGF0ZSBhdmVyYWdlIHJhdGlvcyBwZXIgcmVnaW9uDQoNClRoZSBob3NwaXRhbCBiZWQgZGF0YXNldCBjb250YWlucyByZWNvcmRzIGZvciBlYWNoIGhvc3BpdGFsLiBGb3IgbWFwcGluZywgZWFjaCBtYXAgcmVnaW9uIHdpbGwgbmVlZCB0byByZXByZXNlbnQgdGhlIGF2ZXJhZ2UgDQpvZiBhbGwgaG9zcGl0YWxzIHByZXNlbnQgaW4gdGhhdCByZWdpb24uDQoNCg0KIyMgUG9wdWxhdGlvbiBDZW5zdXMgRGF0YQ0KDQpQb3B1bGF0aW9uIGRhdGEgaXMgb2J0YWluZWQgZnJvbSB0aGUgVW5pdGVkIFN0YXRlcyBDZW5zdXMgQnVyZWF1IHVzaW5nIHRoZWlyIDIwMTAgWkNUQSB0byBDb3VudHkgUmVsYXRpb25zaGlwIEZpbGUgKHpjdGFfY291bnR5X3JlbF8xMC50eHQpDQoNCmh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvZ2VvZ3JhcGhpZXMvcmVmZXJlbmNlLWZpbGVzL3RpbWUtc2VyaWVzL2dlby9yZWxhdGlvbnNoaXAtZmlsZXMuMjAxMC5odG1sI3Bhcl90ZXh0aW1hZ2VfNjc0MTczNjIyDQoNCmRvd25sb2FkOiBodHRwczovL3d3dzIuY2Vuc3VzLmdvdi9nZW8vZG9jcy9tYXBzLWRhdGEvZGF0YS9yZWwvemN0YV9jb3VudHlfcmVsXzEwLnR4dA0KY29sdW1uIGRlc2NyaXB0aW9uczogaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL2dlb2dyYXBoeS90ZWNobmljYWwtZG9jdW1lbnRhdGlvbi9yZWNvcmRzLWxheW91dC8yMDEwLXpjdGEtcmVjb3JkLWxheW91dC5odG1sI3Bhcl90ZXh0aW1hZ2VfMA0KDQojIyBHZW9ncmFwaGljIFNoYXBlIERhdGENCg0KIyMjIyAqVVMgQ291bnR5IFNoYXBlIERhdGEqDQoNCkNvdW50eSBzaGFwZSBkYXRhIGlzIG9idGFpbmVkIGZyb20gdGhlIFIgcGFja2FnZSBgQWxiZXJzdXNhYC4NCg0KIyMjIyAqSG9zcGl0YWwgUmVmZXJyYWwgUmVnaW9uIFNoYXBlIERhdGEqDQoNClNoYXBlIGRhdGEgZm9yIFVTQSBIUlIgcmVnaW9ucyBhcmUgZG93bmxvYWRlZCBmcm9tIGFyY2dpcy5jb20gdXNpbmcgdGhlaXIgIkZlYXR1cmVTZXJ2ZXIiIFJFU1QgYXBpLg0KDQpodHRwczovL3d3dy5hcmNnaXMuY29tL2hvbWUvaXRlbS5odG1sP2lkPTQ2YmY2NzkwYzRlMDQ1NWU5Mzc5ZWU5NzY5YjFhNWFiDQoNCg0KIyMgQ3Jvc3N3YWxrIERhdGENCg0KIyMjIyAqSFJSIG51bWJlciB0byB6aXAgY29kZSB0cmFuc2xhdGlvbiAoMjAxOSkqDQoNClRoaXMgY3Jvc3N3YWxrIGlzIG9idGFpbmVkIGZyb20gRGFydG1vdXRoIEF0bGFzIGFzIGEgemlwIGZpbGUuDQoNCmh0dHBzOi8vZGF0YS5kYXJ0bW91dGhhdGxhcy5vcmcvc3VwcGxlbWVudGFsLyNjcm9zc3dhbGtzDQoNCg0KIyBMb2FkIFJlcXVpcmVkIERhdGENCg0KIyMjIEdldCBHZW9ncmFwaGljLCBDZW5zdXMsIGFuZCBDcm9zc3dhbGsgRGF0YQ0KDQpWYWNjaW5hdGlvbiBkYXRhIGFuZCBob3NwaXRhbCBiZWQgdXNhZ2UgZGF0YSBhcmUgbG9hZGVkIGR1cmluZyB0aGUgY2FsbCB0byB0aGUgZ3JhcGhpbmcNCmZ1bmN0aW9uIHNvIHRoYXQgb25seSB0aGUgZGF0ZSBuZWVkZWQgaXMgbG9hZGVkIGludG8gbWVtb3J5Lg0KDQpgYGB7cn0NCiMgU2V0dXAgdGhlIGRhdGEgY2FjaGluZyBmb2xkZXINCmNyZWF0ZV9kYXRhX2ZvbGRlcl9pZl9kbmUoKQ0KDQojIGdldCBjb3VudHkgc2hhcGVzIGZvciBtYXBwaW5nDQp1c19jb3VudHlfc2hhcGVfZGF0YSA8LSBjb3VudGllc19zZigibGFlYSIpICU+JSANCiAgbXV0YXRlKGZpcHMgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihmaXBzKSkpDQoNCiMgZ2V0IHN0YXRlIHNoYXBlcyBmb3IgbWFwcGluZw0KdXNfc3RhdGVfc2hhcGVfZGF0YSA8LSB1c2Ffc2YoImxhZWEiKQ0KDQojIEdldCBocnIgc2hhcGVzIGZvciBtYXBwaW5nDQpocnJfc2hhcGVfZGF0YSA8LSBnZXRfaHJyX3NoYXBlc19hcmNnaXMoKQ0KDQojIEdldCBSZXF1aXJlZCBDcm9zc3dhbGtzDQp6aXBfaHJyX2Nyb3Nzd2Fsa19kYXRhIDwtIGdldF9ocnJfemlwX2Nyb3Nzd2FsaygpDQoNCiMgZ2V0IHBvcHVsYXRpb24gY2Vuc3VzIGRhdGENCnpjdGFfcG9wdWxhdGlvbl9kYXRhIDwtIGdldF96Y3RhX3BvcHVsYXRpb25fZGF0YSgpDQoNCmhycl90b19zdGF0ZSA8LSBocnJfc2hhcGVfZGF0YSAlPiUgDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUgDQogIHNlbGVjdChIUlJOVU0sIEhSUlNUQVRFX2xvbmcpDQoNCmBgYA0KDQojIyMgQ2FsY3VsYXRlIEhSUiBwb3B1bGF0aW9uDQoNCkEgZGF0YXNldCBpcyBuZWVkZWQgdGhhdCB0cmFja3MgdGhlIHBvcHVsYXRpb24gcGVyY2VudGFnZXMgb2YgZWFjaCBaQ1RBIGluIGVhY2ggY291bnR5IGZvciB0aGUgZnVuY3Rpb24gdGhhdCBjYWxjdWxhdGVzIHZhY2NpbmF0aW9uIHJhdGVzIG9mIEhSUnMNCg0KYGBge3J9DQoNCg0KY2FsY3VsYXRlX2hycl9wb3B1bGF0aW9uX3ppcF9zbGljZXMgPC0gZnVuY3Rpb24oKXsNCiAjIGdldCBwb3B1bGF0aW9uIHRvdGFscyBvZiBocnINCiAgaHJyX3BvcHVsYXRpb25femlwX3NsaWNlID0gemlwX2hycl9jcm9zc3dhbGtfZGF0YSAlPiUgDQogICAgbGVmdF9qb2luKA0KICAgICAgemN0YV9wb3B1bGF0aW9uX2RhdGEsIA0KICAgICAgYnkgPSBjKCJ6aXBjb2RlMTkiID0gIlpDVEE1IikpICU+JQ0KICAgIGdyb3VwX2J5KGhycm51bSkgJT4lIA0KICAgIHN1bW1hcmlzZShwb3B1bGF0aW9uID0gc3VtKFBPUFBULCBuYS5ybSA9IFQpLCB6aXBfY291bnQgPSBuKCkpDQogIA0KICAjIyBjaGVjayBob3cgd2VsbCB0aGUgam9pbiB3b3JrZWQgIyMNCiAgDQogIHRvdGFsX3pjdGFfcG9wID0gc3VtKHpjdGFfcG9wdWxhdGlvbl9kYXRhJFBPUFBULCBuYS5ybSA9IFQpDQogIHVzX3RlcnJpdG9yaWVzX3BvcF93aWtpID0gMzYyMzg5NQ0KICB1c2FfemN0YV9wb3AgPSB0b3RhbF96Y3RhX3BvcCAtIHVzX3RlcnJpdG9yaWVzX3BvcF93aWtpDQogIA0KICB0b3RhbF9ocnJfcG9wID0gc3VtKGhycl9wb3B1bGF0aW9uX3ppcF9zbGljZQ0KICAgICAgICAgICAgICAgICAgICAkcG9wdWxhdGlvbiwgbmEucm0gPSBUKQ0KICANCiAgcGVyY2VudF9yZXRhaW5lZCA9IGZvcm1hdCh0b3RhbF9ocnJfcG9wIC8gdG90YWxfemN0YV9wb3AgKiAxMDAsIGRpZ2l0cyA9IDQpIA0KICBudW1iZXJfbG9zdCA9IGZvcm1hdCh0b3RhbF96Y3RhX3BvcCAtIHRvdGFsX2hycl9wb3AsIGJpZy5tYXJrPSIsIikNCiAgDQogIHByaW50KHBhc3RlKA0KICAgICJBZnRlciBqb2luaW5nIFVTIHN0YXRlIHppcCBjb2RlcyB3aXRoIFpDVEFzLCBXZSBrZWVwIGFib3V0IiwgDQogICAgcGVyY2VudF9yZXRhaW5lZCwgDQogICAgIiUgb2YgdGhlIHBvcHVsYXRpb24sIGxvb3NpbmciLCANCiAgICBudW1iZXJfbG9zdCwgInBlb3BsZS4iICkpDQogIA0KICBwcmludChwYXN0ZSgNCiAgICAiemN0YXMgYWxzbyBhY2NvdW50IGZvciB0aGUgcG9wdWxhdGlvbiBpbiB1cyB0ZXJyaXRvcmllcywgd2hpY2ggaGF2ZSBhIHRvdGFsIHBvcHVsYXRpb24gb2YiLCANCiAgICBmb3JtYXQodXNfdGVycml0b3JpZXNfcG9wX3dpa2ksIGJpZy5tYXJrID0gIiwiKSwgImFjY29yZGluZyB0byB3aWtpcGVkaWEuIikpDQogIHByaW50KHBhc3RlKA0KICAgICJUaGlzIHB1c2hlcyB0aGUgcmV0YWluZWQgcG9wb3VsYXRpb24gb2YgdGhlIHN0YXRlcyBjbG9zZXIgdG8iLA0KICAgIGZvcm1hdCh0b3RhbF9ocnJfcG9wIC8gdXNhX3pjdGFfcG9wICogMTAwLCBkaWdpdHMgPSA0KSwgInBlcmNlbnQiICkpDQogIA0KICByZXR1cm4oaHJyX3BvcHVsYXRpb25femlwX3NsaWNlKQ0KfQ0KDQogDQpocnJfcG9wdWxhdGlvbl96aXBfc2xpY2UgPC0gY2FsY3VsYXRlX2hycl9wb3B1bGF0aW9uX3ppcF9zbGljZXMoKQ0KDQpgYGANCiMjIyAqKkNhbGN1bGF0ZSBISFIgdmFjY2luYXRpb24gcmF0ZSoqDQoNCioqV2UgYXJlIHVzaW5nIHBvcHVsYXRpb24gZGF0YSBmb3IgemlwIGNvZGUgdGFidWxhdGlvbiBhcmVhcyBiZWNhdXNlIHdlIGRvbid0IGhhdmUgY291bnRzIGZvciB6aXAgY29kZXMuIFpDVEFzIHdpbGwgYmUgb3VyIHByb3h5IGZvciB6aXAgY29kZXMuIEJlY2F1c2UgemN0YSdzIGRvbid0IGxpbmUgdXAgZXhhY3RseSB3aXRoIHppcCBjb2RlcywgdGhlIHNhbWUgemN0YSBjYW4gYmUgcGFydCBvZiBtb3JlIHRoYW4gb25lIGNvdW50eS4gVGhhdCBpcyB3aHkgaXQgaXMgaW1wb3J0YW50IHRvIGtub3cgaG93IG11Y2ggb2YgdGhlIFpDVEEncyBwb3B1bGF0aW9uIGlzIGluIGVhY2ggY291bnR5LioqDQoNCipUaGUgUHJvY2VzczoqDQoNCjEuIEpvaW4gdGhlIHZhY2NpbmF0aW9uIGRhdGEgZm9yIDIwMjEvMDkvMjQgd2l0aCBwb3B1bGF0aW9uIHppcCBjb2RlIGRhdGEgYnkgY291bnR5Lg0KICAqICpOb3cgd2Uga25vdyB0aGUgdmFjY2luYXRpb24gcmF0ZSBvZiBlYWNoIHppcCBjb2RlKg0KMi4gSm9pbiB3aXRoIHRoZSB6aXAgaGhyIGNyb3Nzd2FsayBieSB6aXAgY29kZSB0byB6Y3RhLg0KICAqICpOb3cgd2Uga25vdyB0aGUgaHJyIGVhY2ggemlwIGNvZGUgaXMgaW4gYWxvbmcgd2l0aCB0aGF0IHppcCBjb2RlJ3MgdmFjY2luYXRpb24gcmF0ZSoNCjMuIEpvaW4gd2l0aCB0aGUga25vd24gcG9wdWxhdGlvbiBjb3VudHMgb2YgemlwIGNvZGVzIGJ5IHppcCBjb2RlLg0KICAqICpOb3cgd2Uga25vdyB0aGUgc2xpY2Ugb2YgcG9wdWxhdGlvbiBvZiBhIHppcCBjb2RlIGluc2lkZSBlYWNoIGNvdW50eSBhbG9uZyB3aXRoIHRoYXQgcG9wdWxhdGlvbiBzbGljZSdzIHZhY2NpbmF0aW9uIHJhdGUqDQo0LiBDYWxjdWxhdGUgbnVtYmVyIG9mIHZhY2NpbmF0ZWQgcGVvcGxlIGluIGVhY2ggemlwIGNvZGUncyBwb3B1bGF0aW9uIHNsaWNlIGluc2lkZSBlYWNoIGNvdW50eSBmb3IgZWFjaCBocnINCjUuIERpdmlkZSB0aGUgbnVtYmVyIG9mIHZhY2NpbmF0ZWQgaW4gemlwIHNsaWNlIGJ5IHRoZSB0b3RhbCBocnIgcG9wdWxhdGlvbiB0aGF0IHppcCBjb2RlIGlzIGluLg0KICAqICpUaGlzIGdpdmVzIHVzIHRoZSAic2xpY2UgdmFjY2luYXRpb24gcGVyY2VudGFnZSIgdGhhdCBjb250cmlidXRlcyB0byB0aGUgdmFjY2luYXRpb24gcGVyY2VudGFnZSBvZiB0aGUgaHJyKg0KNi4gQWRkIHVwIGFsbCBvZiB0aGUgc2xpY2VkIHZhY2NpbmF0aW9uIHBlcmNlbnRhZ2VzIGZvciBlYWNoIGhyci4NCg0KDQoNCmBgYHtyfQ0KDQpjYWxjdWxhdGVfaHJyX3ZhY2NpbmF0aW9uX3JhdGVzIDwtIGZ1bmN0aW9uKGRhdGUpIHsNCg0KICB2YWNjaW5hdGlvbl9kYXRhID0gZ2V0X3ZhY2NpbmF0aW9uX3JhdGVzX2RhdGEoZGF0ZSkNCiAgDQogIHppcF9ocnJfdmFjY2luYXRpb25fcG9wdWxhdGlvbl9qb2luID0gdmFjY2luYXRpb25fZGF0YSAlPiUgDQogICAgaW5uZXJfam9pbih6Y3RhX3BvcHVsYXRpb25fZGF0YSwgYnkgPSBjKCJmaXBzIiA9ICJHRU9JRCIpKSAlPiUgDQogICAgaW5uZXJfam9pbih6aXBfaHJyX2Nyb3Nzd2Fsa19kYXRhLCBieSA9IGMoIlpDVEE1IiA9ICJ6aXBjb2RlMTkiKSkgJT4lIA0KICAgIHNlbGVjdChaQ1RBNSwgZmlwcywgUE9QUFQsIHNlcmllc19jb21wbGV0ZV9wb3BfcGN0LCBhZG1pbmlzdGVyZWRfZG9zZTFfcG9wX3BjdCwgaHJybnVtKSAlPiUgDQogICAgDQogICAgcmVuYW1lKA0KICAgICAgemN0YSA9IFpDVEE1LCANCiAgICAgIHppcF9wb3Bfc2xpY2VfaW5fZmlwcyA9IFBPUFBULCANCiAgICAgIGZpcHNfdmFjY2luYXRpb25fcGVyY2VudCA9IHNlcmllc19jb21wbGV0ZV9wb3BfcGN0LA0KICAgICAgZmlwc19vbmVfZG9zZV9wZXJjZW50ID0gYWRtaW5pc3RlcmVkX2Rvc2UxX3BvcF9wY3QpICU+JSANCiAgICANCiAgICBpbm5lcl9qb2luKGhycl9wb3B1bGF0aW9uX3ppcF9zbGljZSwgYnkgPSAiaHJybnVtIikgJT4lIA0KICAgIHJlbmFtZSh0b3RhbF9ocnJfcG9wdWxhdGlvbiA9IHBvcHVsYXRpb24pDQogIA0KICBocnJfdmFjY19wZXJjZW50ID0gemlwX2hycl92YWNjaW5hdGlvbl9wb3B1bGF0aW9uX2pvaW4gJT4lIA0KICAgIG11dGF0ZSgNCiAgICAgIG51bV92YWNjX2luX3ppcF9zbGljZSA9IGZpcHNfdmFjY2luYXRpb25fcGVyY2VudCAqIHppcF9wb3Bfc2xpY2VfaW5fZmlwcywgICAgICAgICAgICMgY2FsY3VsYXRlIG51bWJlciBpbiB6aXAgb2YgZnVsbHkgdmFjY2luYXRlZA0KICAgICAgbnVtX29uZV9kb3NlX2luX3ppcF9zbGljZSA9IGZpcHNfb25lX2Rvc2VfcGVyY2VudCAqIHppcF9wb3Bfc2xpY2VfaW5fZmlwcykgJT4lICAgICAgICMgY2FsY3VsYXRlIG51bWJlciBpbiB6aXAgb2Ygb25lIGRvc2UNCiAgICBtdXRhdGUoDQogICAgICB6aXBfc2xpY2Vfd2VpZ2h0ZWRfdmFjY19wZXJjZW50ID0gbnVtX3ZhY2NfaW5femlwX3NsaWNlIC8gdG90YWxfaHJyX3BvcHVsYXRpb24sICAgICAgICAgICAgICAgICAgIyBjYWxjdWxhdGUgd2VpZ2h0ZWQgcG9ydGlvbiBvZiBmdWxseSB2YWNjaW5hdGVkDQogICAgICB6aXBfc2xpY2Vfd2VpZ2h0ZWRfc2luZ2xlX2Rvc2VfcGVyY2VudCA9IG51bV9vbmVfZG9zZV9pbl96aXBfc2xpY2UgLyB0b3RhbF9ocnJfcG9wdWxhdGlvbikgJT4lICAgIyBjYWxjdWxhdGUgd2VpZ2h0ZWQgcG9ydGlvbiBvZiBzaW5nbGUgZG9zZQ0KICAgIHNlbGVjdChocnJudW0sIHppcF9zbGljZV93ZWlnaHRlZF92YWNjX3BlcmNlbnQsIHppcF9zbGljZV93ZWlnaHRlZF9zaW5nbGVfZG9zZV9wZXJjZW50KSAlPiUgDQogICAgZ3JvdXBfYnkoaHJybnVtKSAlPiUgDQogICAgc3VtbWFyaXNlKA0KICAgICAgdmFjY19jb21wbGV0ZV9wZXJjZW50ID0gc3VtKHppcF9zbGljZV93ZWlnaHRlZF92YWNjX3BlcmNlbnQpLA0KICAgICAgc2luZ2xlX2Rvc2VfcGVyY2VudCA9IHN1bSh6aXBfc2xpY2Vfd2VpZ2h0ZWRfc2luZ2xlX2Rvc2VfcGVyY2VudCkpDQp9DQoNCg0KYGBgDQoNCg0KIyMjIEdyYXBoIGJ5IERhdGUgRnVuY3Rpb25zDQoNClRoZXNlIGZ1bmN0aW9ucyByZXRyaWV2ZSBkYXRhIGZvciB2YWNjaW5hdGlvbiByYXRlcyBhbmQgaG9zcGl0YWwgYmVkIHVzYWdlIGZyb20gdGhlaXIgcmVzcGVjdGl2ZSBBUElzDQoNCg0KYGBge3J9DQoNCnBvc3NpYmxlX2F4aXNfbGFiZWxzIDwtIGMoDQogICJ2YWNjX2NvbXBsZXRlX3BlcmNlbnQiICAgICAgICAgICAgICAgICA9ICJQb3B1bGF0aW9uIEZ1bGx5IFZhY2NpbmF0ZWQiLA0KICAic2luZ2xlX2Rvc2VfcGVyY2VudCIgICAgICAgICAgICAgICAgICAgPSAiUG9wdWxhdGlvbiBXaXRoIFNpbmdsZSBEb3NlIiwNCiAgImJlZF91c2FnZV9yYXRpbyIgICAgICAgICAgICAgICAgICAgICAgID0gIkhvc3BpdGFsIEJlZHMgVXNlZCIsDQogICJjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8iICAgICAgICAgICAgICAgICA9ICJIb3NwaXRhbCBCZWRzIFVzZWQgZm9yIENvdmlkIiwNCiAgImNvdmlkX2JlZF91c2FnZV90b3RhbF9iZWRfdXNhZ2VfcmF0aW8iID0gIkNvdmlkIEJlZCB0byBUb3RhbCBCZWQgVXNhZ2UiKQ0KDQoNCiMjIyBNZXRob2QgZm9yIHNlbGVjdGluZyBhcHByb3ByaWF0ZSBkYXRlcyBmb3IgZGF0YQ0KDQp2YWxpZF92YWNjaW5hdGlvbl9kYXRlcyA8LSByZWFkX2NzdigidmFsaWRfdmFjY2luYXRpb25fZGF0ZXMuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGKSREYXRlDQp2YWxpZF9iZWRfdXNhZ2VfZGF0ZXMgPC0gcmVhZF9jc3YoInZhbGlkX2JlZF91c2FnZV9kYXRlcy5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEYpJGNvbGxlY3Rpb25fd2Vlaw0KDQpjbG9zZXN0X3ZhbGlkX2RhdGVzIDwtIGZ1bmN0aW9uKGRhdGUsIGxpc3QpIHsNCiAgDQogIGNsb3Nlc3RfaW5kZXggPSB3aGljaC5taW4oYWJzKHltZChkYXRlKSAtIHZhbGlkX3ZhY2NpbmF0aW9uX2RhdGVzKSkNCiAgdmFjX2RhdGUgPSB2YWxpZF92YWNjaW5hdGlvbl9kYXRlc1tjbG9zZXN0X2luZGV4XQ0KICANCiAgY2xvc2VzdF9pbmRleCA9IHdoaWNoLm1pbihhYnMoeW1kKGRhdGUpIC0gdmFsaWRfYmVkX3VzYWdlX2RhdGVzKSkNCiAgYmVkX2RhdGUgPSB2YWxpZF9iZWRfdXNhZ2VfZGF0ZXNbY2xvc2VzdF9pbmRleF0NCiAgDQogIHJldHVybihjKHZhY19kYXRlLCBiZWRfZGF0ZSkpDQogIA0KfQ0KDQpncmFwaF9pbnRlcmFjdGl2ZSA8LSBmdW5jdGlvbihncmFwaCl7DQogIGdncGxvdGx5KGdyYXBoLCB0b29sdGlwID0gInRleHQiKSAlPiUgDQogICAgc3R5bGUoaG92ZXJvbiA9ICJmaWxsIikgDQp9DQpgYGANCg0KDQojIyMjIFBvaW50IFBsb3Qgb2YgVmFjY2luYXRpb24gYW5kIEhvc3BpdGFsaXphdGlvbiBTdGF0cw0KDQoNCmBgYHtyfQ0KDQojIyMgVmFjY2luYXRpb24gUmF0ZSBieSBISFIgR3JhcGhpbmcgRnVuY3Rpb24NCg0KIyMjIFN0YXQgU2VsZWN0YWJsZSBJbnRlcmFjdGl2ZSBHcmFwaCBvZiBWYWNjaW5hdGlvbiBSYXRlcyBieSBIUlINCiMNCiMgVmFsdWVzIGZvciBgeF9heGlzYCBhbmQgYHlfYXhpc2A6DQojICAgICAgICAgdmFjY19jb21wbGV0ZV9wZXJjZW50OiBQZXJjZW50YWdlIG9mIHBlb3BsZSBmdWxseSB2YWNjaW5hdGVkIGluIHRoYXQgSFJSDQojICAgICAgICAgc2luZ2xlX2Rvc2VfcGVyY2VudDogICBQZXJjZW50YWdlIG9mIHBlb3BsZSB3aXRoIG9uZSB2YWNjaW5lIGRvc2UgaW4gdGhhdCBIUlINCiMgICAgICAgICBiZWRfdXNhZ2VfcmF0aW86ICAgICAgICAgICAgICAgICAgICAgICAgIFBlcmNlbnRhZ2Ugb2YgaG9zcGl0YWwgYmVkcyB1c2VkIGluIEhSUg0KIyAgICAgICAgIGNvdmlkX2JlZF91c2FnZV9yYXRpbzogICAgICAgICAgICAgICAgICAgUGVyY2VudGFnZSBvZiBiZWRzIHVzZWQgZm9yIENPVklEIGluIEhSUg0KIyAgICAgICAgIGNvdmlkX2JlZF91c2FnZV90b3RhbF9iZWRfdXNhZ2VfcmF0aW86ICAgUmF0aW8gb2YgQ09WSUQgYmVkIHVzYWdlIHRvIHRvdGFsIGJlZCB1c2FnZQ0KIw0KIyBEYXRlIG11c3QgYmUgZ2l2ZW4gaW4geXl5eW1tZGQgZm9ybWF0DQpHZW5lcmF0ZV9WYWNjaW5hdGlvbl9QbG90IDwtIGZ1bmN0aW9uKGRhdGUsIHhfYXhpcywgeV9heGlzKXsNCiAgDQogIHhfYXhpc19zdGF0ID0gcmxhbmc6OnBhcnNlX2V4cHIoeF9heGlzKQ0KICB5X2F4aXNfc3RhdCA9IHJsYW5nOjpwYXJzZV9leHByKHlfYXhpcykNCg0KICB4X2F4aXNfbGFiZWwgPSBwYXN0ZTAocG9zc2libGVfYXhpc19sYWJlbHNbeF9heGlzXSkNCiAgeV9heGlzX2xhYmVsID0gcGFzdGUwKHBvc3NpYmxlX2F4aXNfbGFiZWxzW3lfYXhpc10pICANCg0KICBkYXRlcyA9IGNsb3Nlc3RfdmFsaWRfZGF0ZXMoeW1kKGRhdGUpKQ0KICANCiAgIyBHZXQgVmFjY2luYXRpb24gRGF0YQ0KICB2YWNjaW5hdGlvbl9kYXRhX3Blcl9ocnIgPSBjYWxjdWxhdGVfaHJyX3ZhY2NpbmF0aW9uX3JhdGVzKGRhdGVzWzFdKQ0KICAgIA0KICAjIyBHZXQgSG9zcGl0YWwgRGF0YQ0KICBob3NwaXRhbF9iZWRfZGF0YV9wZXJfaG9zcGl0YWwgPSBnZXRfYmVkX3V0aWxpemF0aW9uX2RhdGEoZGF0ZXNbMl0pICU+JSAgIyBkb24ndCBpbmNsdWRlIGhvc3BpdGFscyB3aXRoIDAgYmVkcw0KICAgIGZpbHRlcihpbnBhdGllbnRfYmVkc183X2RheV9hdmcgPiAxLCBzdGF0ZSAhPSAiVFgiKSAgICAgICAgICAgICAgICAgICAgIyBUWCBkb2Vzbid0IGhhdmUgdmFjY2luYXRpb24gZGF0YQ0KICANCiAgIyBjYWxjdWxhdGUgYmVkIHVzYWdlIHJhdGlvcw0KICBob3NwaXRhbF9iZWRfZGF0YV9wZXJfaG9zcGl0YWwgPSBob3NwaXRhbF9iZWRfZGF0YV9wZXJfaG9zcGl0YWwgJT4lIA0KICAgIG11dGF0ZSgNCiAgICAgIGJlZF91c2FnZV9yYXRpbyA9IGlucGF0aWVudF9iZWRzX3VzZWRfN19kYXlfYXZnIC8gaW5wYXRpZW50X2JlZHNfN19kYXlfYXZnICogMTAwLA0KICAgICAgY292aWRfYmVkX3VzYWdlX3JhdGlvID0gaW5wYXRpZW50X2JlZHNfdXNlZF9jb3ZpZF83X2RheV9hdmcgLyBpbnBhdGllbnRfYmVkc183X2RheV9hdmcgKiAxMDAsDQogICAgICBjb3ZpZF9iZWRfdXNhZ2VfdG90YWxfYmVkX3VzYWdlX3JhdGlvID0gaW5wYXRpZW50X2JlZHNfdXNlZF9jb3ZpZF83X2RheV9hdmcgLyBpbnBhdGllbnRfYmVkc191c2VkXzdfZGF5X2F2Zw0KICAgICkNCiAgDQogICMgR3JvdXAgSG9zcGl0YWxzIGluIEhSUg0KICBiZWRfcmF0aW9zX3Blcl9ocnIgPSBob3NwaXRhbF9iZWRfZGF0YV9wZXJfaG9zcGl0YWwgJT4lIA0KICAgIGxlZnRfam9pbih6aXBfaHJyX2Nyb3Nzd2Fsa19kYXRhLCBieSA9IGMoInppcCIgPSAiemlwY29kZTE5IikpICU+JSANCiAgICBncm91cF9ieShocnJudW0pICU+JSANCiAgICBzdW1tYXJpc2UoDQogICAgICBiZWRfdXNhZ2VfcmF0aW8gPSBtZWFuKGJlZF91c2FnZV9yYXRpbywgbmEucm0gPSBUUlVFKSwgDQogICAgICBjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8gPSBtZWFuKGNvdmlkX2JlZF91c2FnZV9yYXRpbywgbmEucm0gPSBUUlVFKSwgDQogICAgICBjb3ZpZF9iZWRfdXNhZ2VfdG90YWxfYmVkX3VzYWdlX3JhdGlvID0gbWVhbihjb3ZpZF9iZWRfdXNhZ2VfdG90YWxfYmVkX3VzYWdlX3JhdGlvLCBuYS5ybSA9IFRSVUUpKQ0KICAgIA0KICAjIENvbWJpbmUgVmFjY2luYXRpb24gYW5kIEJlZCBEYXRhIChhbmQgaHJyL3N0YXRlIGNyb3Nzd2FsaykNCiAgYmVkX3V0aWxpemF0aW9uX3ZhY2NpbmF0aW9uX2RhdGFfaHJyID0gYmVkX3JhdGlvc19wZXJfaHJyICU+JSANCiAgICBsZWZ0X2pvaW4odmFjY2luYXRpb25fZGF0YV9wZXJfaHJyLCBieSA9ICJocnJudW0iKSAlPiUgDQogICAgbGVmdF9qb2luKGhycl9wb3B1bGF0aW9uX3ppcF9zbGljZSwgYnkgPSAiaHJybnVtIikgJT4lIA0KICAgIGxlZnRfam9pbihocnJfdG9fc3RhdGUsIGJ5ID0gYygiaHJybnVtIiA9ICJIUlJOVU0iKSkNCiAgDQogIHBsb3RfZGF0YSA9IGJlZF91dGlsaXphdGlvbl92YWNjaW5hdGlvbl9kYXRhX2hyciAlPiUgDQogICAgZHJvcF9uYSgpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2Ryb3AgdW5wbG90dGFibGUgcm93cw0KICAgIA0KICAgIG11dGF0ZSh0ZXh0ID0gcGFzdGUwKA0KICAgICAgIkhSUiAjOiAiLCBocnJudW0sDQogICAgICAiPC9iPlxuU3RhdGU6ICIsIEhSUlNUQVRFX2xvbmcsIA0KICAgICAgIjwvYj5cbkZ1bGx5IFZhY2NpbmF0ZWQ6ICIsIGZvcm1hdCh2YWNjX2NvbXBsZXRlX3BlcmNlbnQsIGRpZ2l0cyA9IDIpLCAiJSIsDQogICAgICAiPC9iPlxuSGFkIFNpbmdsZSBEb3NlOiAiLCBmb3JtYXQoc2luZ2xlX2Rvc2VfcGVyY2VudCwgZGlnaXRzID0gMSksICIlIiwNCiAgICAgICI8L2I+XG5aaXAgQ29kZSBDb3VudDogIiwgemlwX2NvdW50LA0KICAgICAgIjwvYj5cbkhSUiBQb3A6ICIsIGZvcm1hdChwb3B1bGF0aW9uLCBiaWcubWFyayA9ICIsIikpKSAlPiUgDQogICAgbXV0YXRlKHZhY2NfY29tcGxldGVfcGVyY2VudCA9IHZhY2NfY29tcGxldGVfcGVyY2VudCAvIDEwMCwNCiAgICAgICAgICAgc2luZ2xlX2Rvc2VfcGVyY2VudCA9IHNpbmdsZV9kb3NlX3BlcmNlbnQvMTAwLA0KICAgICAgICAgICBjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8gPSBjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8vMTAwLA0KICAgICAgICAgICBiZWRfdXNhZ2VfcmF0aW8gPSBiZWRfdXNhZ2VfcmF0aW8vMTAwKSAlPiUgDQogICAgDQogICAgZ2dwbG90KGFlcyh4ID0gISF4X2F4aXNfc3RhdCwgeSA9ICEheV9heGlzX3N0YXQpKSArDQogICAgICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuNCkgKw0KICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwb3B1bGF0aW9uLCB0ZXh0PXRleHQpLCBzaXplID0gMS4yKSArDQogICAgICBzY2FsZV9jb2xvcl9jb250aW51b3VzKA0KICAgICAgICAgICJIUlIgUG9wdWxhdGlvbiIsIA0KICAgICAgICAgIHRyYW5zID0gImxvZzEwIiwNCiAgICAgICAgICB0eXBlID0gImdyYWRpZW50IiwgDQogICAgICAgICAgbGFiZWxzID0gc2NhbGVzOjpjb21tYSwNCiAgICAgICAgICBsb3cgPSAiYmx1ZSIsDQogICAgICAgICAgaGlnaCA9ICJnb2xkIikgKw0KICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEYsIGxpbmV3aWR0aCA9IDAuNSkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyh4X2F4aXNfbGFiZWwsIGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoeV9heGlzX2xhYmVsLCBsYWJlbHM9c2NhbGVzOjpwZXJjZW50KQ0KfQ0KDQpgYGANCmANCg0KDQojIyMjIE1hcCBCeSBEYXRlIEdyYXBocw0KDQpgYGB7cn0NCg0KZ3JhcGhfaW50ZXJhY3RpdmUgPC0gZnVuY3Rpb24oZ3JhcGgpew0KICBnZ3Bsb3RseShncmFwaCwgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lIA0KICAgIHN0eWxlKGhvdmVyb24gPSAiZmlsbHMiKSANCn0NCg0KIyMjIyMgVkFDQ0lOQVRJT04gUkFURQ0KDQojIyMjIw0KIyBEYXRlIG11c3QgYmUgZ2l2ZW4gaW4geXl5eW1tZGQgZm9ybWF0DQpHcmFwaF9WYWNjaW5hdGlvbl9SYXRlc19CeV9Db3VudHkgPC0gZnVuY3Rpb24oZGF0ZSkgew0KICB2YWNjaW5hdGlvbl9kYXRhID0gZ2V0X3ZhY2NpbmF0aW9uX3JhdGVzX2RhdGEoZGF0ZSA9IGRhdGUpDQogIA0KICB2YWNjaW5hdGlvbl9kYXRhID0gdXNfY291bnR5X3NoYXBlX2RhdGEgJT4lIA0KICAgIGxlZnRfam9pbih2YWNjaW5hdGlvbl9kYXRhLCBieSA9ICJmaXBzIikgDQogICAgDQogIHZhY2NpbmF0aW9uX2RhdGEgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKGFlcyhmaWxsID0gc2VyaWVzX2NvbXBsZXRlX3BvcF9wY3QvMTAwKSkgKw0KICAgIHNjYWxlX2ZpbGxfY29udGludW91cygiRnVsbHkgVmFjY2luYXRlZCIsIGxvdz0icmVkIiwgaGlnaD0ieWVsbG93IiwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogICAgZ2d0aXRsZSgiQ09WSUQgVmFjY2luYXRpb24gU3RhdHVzIiwgc3VidGl0bGUgPSAiUGVyY2VudGFnZSBvZiBjb3VudHkgcG9wdWxhdGlvbiB0aGF0IGlzIGZ1bGx5IHZhY2NpbmF0ZWQgKGZyb20gQ0RDKSIpICsNCiAgICBteV9tYXBfdGhlbWUoKQ0KfQ0KDQojIERhdGUgbXVzdCBiZSBnaXZlbiBpbiB5eXl5bW1kZCBmb3JtYXQNCkdyYXBoX1ZhY2NpbmF0aW9uX1JhdGVzX0J5X0hyciA8LSBmdW5jdGlvbihkYXRlKSB7DQogIHZhY2NpbmF0aW9uX2RhdGEgPC0gY2FsY3VsYXRlX2hycl92YWNjaW5hdGlvbl9yYXRlcyhkYXRlKQ0KICANCiAgaHJyX2dncGxvdF9kYXRhID0gaHJyX3NoYXBlX2RhdGEgJT4lIA0KICAgIGxlZnRfam9pbih2YWNjaW5hdGlvbl9kYXRhLCBieSA9IGMoIkhSUk5VTSIgPSAiaHJybnVtIikpICU+JSANCiAgICBzZWxlY3QoIUhSUikgICU+JSANCiAgICBzdF90cmFuc2Zvcm0oY3JzPSAiRVBTRzoyMTYzIikgJT4lIA0KICAgICAgbXV0YXRlKHRleHQgPSBwYXN0ZTAoDQogICAgICAiU3RhdGU6ICIsIEhSUlNUQVRFX2xvbmcsIA0KICAgICAgIjwvYj5cbkZ1bGx5IFZhY2NpbmF0ZWQ6ICIsIGZvcm1hdCh2YWNjX2NvbXBsZXRlX3BlcmNlbnQsIGRpZ2l0cyA9IDQpLCAiJSIsDQogICAgICAiPC9iPlxuSGFkIFNpbmdsZSBEb3NlOiAiLCBmb3JtYXQoc2luZ2xlX2Rvc2VfcGVyY2VudCwgZGlnaXRzID0gNCksICIlIiwNCiAgICAgICI8L2I+XG5IUlIgIzogIiwgSFJSTlVNLA0KICAgICAgIjwvYj5cblppcCBDb2RlIENvdW50OiAiLCBocnJfcG9wdWxhdGlvbl96aXBfc2xpY2UkemlwX2NvdW50W0hSUk5VTV0sDQogICAgICAiPC9iPlxuSFJSIFBvcDogIiwgaHJyX3BvcHVsYXRpb25femlwX3NsaWNlJHBvcHVsYXRpb25bSFJSTlVNXSkpDQogIA0KICBocnJfZ2dwbG90X2RhdGEgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICAgIGdlb21fc2YoDQogICAgICAgIGFlcyhmaWxsID0gdmFjY19jb21wbGV0ZV9wZXJjZW50LzEwMCArIHJ1bmlmKG5yb3coaHJyX2dncGxvdF9kYXRhKSwgbWluPTAsIG1heD0wLjAwMSksIA0KICAgICAgICAgICAgdGV4dD10ZXh0KSwgDQogICAgICAgIGxpbmV3aWR0aCA9IDAuMSwgDQogICAgICAgIGNvbG9yPWFscGhhKCJibGFjayIsMC41KSkgKw0KICAgICAgIyBnZW9tX3NmKGRhdGEgPSB1c19zdGF0ZV9zaGFwZV9kYXRhLCBmaWxsID0gYWxwaGEoImJsYWNrIiwgMC4wKSkgKw0KICAgICAgc2NhbGVfZmlsbF9jb250aW51b3VzKCJGdWxseSBWYWNjaW5hdGVkIiwgbG93PSJyZWQiLCBoaWdoPSJ5ZWxsb3ciLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgICAgIGdndGl0bGUoIkNPVklEIFZhY2NpbmF0aW9uIFN0YXR1cyIsIHN1YnRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgY291bnR5IHBvcHVsYXRpb24gdGhhdCBpcyBmdWxseSB2YWNjaW5hdGVkIChmcm9tIENEQykiKSArDQogICAgICBteV9tYXBfdGhlbWUoKQ0KfQ0KDQojIyMjIyBIT1NQSVRBTCBCRUQgVVNBR0UNCg0KIyMjIyMNCiMgRGF0ZSBtdXN0IGJlIGdpdmVuIGluIHl5eXltbWRkIGZvcm1hdA0KR3JhcGhfSG9zcGl0YWxfQmVkX1VzYWdlX0J5X0NvdW50eSA8LSBmdW5jdGlvbihkYXRlKXsNCiAgaG9zcGl0YWxfYmVkX2RhdGEgPSBnZXRfYmVkX3V0aWxpemF0aW9uX2RhdGEoZGF0ZSA9IGRhdGUpDQogIA0KICAjIGNhbGN1bGF0ZSByYXRpb3MNCiAgaG9zcGl0YWxfYmVkX2RhdGEgPSBob3NwaXRhbF9iZWRfZGF0YSAlPiUgDQogICAgbXV0YXRlKA0KICAgICAgYmVkX3VzYWdlX3JhdGlvID0gaW5wYXRpZW50X2JlZHNfdXNlZF83X2RheV9hdmcgLyBpbnBhdGllbnRfYmVkc183X2RheV9hdmcgKiAxMDAsDQogICAgICBjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8gPSBpbnBhdGllbnRfYmVkc191c2VkX2NvdmlkXzdfZGF5X2F2ZyAvIGlucGF0aWVudF9iZWRzXzdfZGF5X2F2ZyAqIDEwMCwNCiAgICAgIGNvdmlkX2JlZF91c2FnZV90b3RhbF9iZWRfdXNhZ2VfcmF0aW8gPSBpbnBhdGllbnRfYmVkc191c2VkX2NvdmlkXzdfZGF5X2F2ZyAvIGlucGF0aWVudF9iZWRzX3VzZWRfN19kYXlfYXZnDQogICAgKQ0KICANCiAgDQogIGNvdW50eV9iZWRfcmF0aW9zID0gaG9zcGl0YWxfYmVkX2RhdGEgJT4lIA0KICAgIGdyb3VwX2J5KGZpcHNfY29kZSkgJT4lIA0KICAgIHN1bW1hcmlzZSgNCiAgICAgIGJlZF91c2FnZV9yYXRpbyA9IG1lYW4oYmVkX3VzYWdlX3JhdGlvLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgIGNvdmlkX2JlZF91c2FnZV9yYXRpbyA9IG1lYW4oY292aWRfYmVkX3VzYWdlX3JhdGlvLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgIGNvdmlkX2JlZF91c2FnZV90b3RhbF9iZWRfdXNhZ2VfcmF0aW8gPSBtZWFuKGNvdmlkX2JlZF91c2FnZV90b3RhbF9iZWRfdXNhZ2VfcmF0aW8sIG5hLnJtID0gVFJVRSkpDQogIA0KICANCiAgDQogIHVzX2NvdW50eV9zaGFwZV9kYXRhICU+JSANCiAgICBsZWZ0X2pvaW4oY291bnR5X2JlZF9yYXRpb3MsIGJ5ID0gYygiZmlwcyIgPSAiZmlwc19jb2RlIikpICU+JSANCiAgICANCiAgICBnZ3Bsb3QoKSArDQogICAgICBnZW9tX3NmKGFlcyhmaWxsID0gY292aWRfYmVkX3VzYWdlX3JhdGlvLzEwMCkpICsNCiAgICAgIHNjYWxlX2ZpbGxfY29udGludW91cygiQmVkcyBVc2VkIGZvciBDb3ZpZCIsIGxvdz0iZGVlcHNreWJsdWUiLCBoaWdoPSJncmVlbiIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICAgICAgZ2d0aXRsZSgiSG9zcGl0YWwgQmVkIFVzYWdlIiwgc3VidGl0bGUgPSAiUGVyY2VudGFnZSBvZiBjb3VudHkncyB0b3RhbCBob3NwaXRhbCBiZWRzIHVzZWQgYnkgY292aWQgcGF0aWVudHMiKSArDQogICAgICBteV9tYXBfdGhlbWUoKQ0KfQ0KDQojIyMjIw0KIyBEYXRlIG11c3QgYmUgZ2l2ZW4gaW4geXl5eW1tZGQgZm9ybWF0DQpHcmFwaF9Ib3NwaXRhbF9CZWRfVXNhZ2VfQnlfSFJSIDwtIGZ1bmN0aW9uKGRhdGUpew0KICBob3NwaXRhbF9iZWRfZGF0YSA9IGdldF9iZWRfdXRpbGl6YXRpb25fZGF0YShkYXRlKQ0KICANCiMgY2FsY3VsYXRlIHJhdGlvcw0KICBob3NwaXRhbF9iZWRfZGF0YSA9IGhvc3BpdGFsX2JlZF9kYXRhICU+JSANCiAgICBtdXRhdGUoDQogICAgICBiZWRfdXNhZ2VfcmF0aW8gPSBpbnBhdGllbnRfYmVkc191c2VkXzdfZGF5X2F2ZyAvIGlucGF0aWVudF9iZWRzXzdfZGF5X2F2ZyAqIDEwMCwNCiAgICAgIGNvdmlkX2JlZF91c2FnZV9yYXRpbyA9IGlucGF0aWVudF9iZWRzX3VzZWRfY292aWRfN19kYXlfYXZnIC8gaW5wYXRpZW50X2JlZHNfN19kYXlfYXZnICogMTAwLA0KICAgICAgY292aWRfYmVkX3VzYWdlX3RvdGFsX2JlZF91c2FnZV9yYXRpbyA9IGlucGF0aWVudF9iZWRzX3VzZWRfY292aWRfN19kYXlfYXZnIC8gaW5wYXRpZW50X2JlZHNfdXNlZF83X2RheV9hdmcNCiAgICApDQogIA0KICBocnJfYmVkX3JhdGlvcyA9IGhvc3BpdGFsX2JlZF9kYXRhICU+JSANCiAgICBsZWZ0X2pvaW4oemlwX2hycl9jcm9zc3dhbGtfZGF0YSwgYnkgPSBjKCJ6aXAiID0gInppcGNvZGUxOSIpKSAlPiUgDQogICAgZ3JvdXBfYnkoaHJybnVtKSAlPiUgDQogICAgc3VtbWFyaXNlKA0KICAgICAgYmVkX3VzYWdlX3JhdGlvID0gbWVhbihiZWRfdXNhZ2VfcmF0aW8sIG5hLnJtID0gVFJVRSksIA0KICAgICAgY292aWRfYmVkX3VzYWdlX3JhdGlvID0gbWVhbihjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8sIG5hLnJtID0gVFJVRSksIA0KICAgICAgY292aWRfYmVkX3VzYWdlX3RvdGFsX2JlZF91c2FnZV9yYXRpbyA9IG1lYW4oY292aWRfYmVkX3VzYWdlX3RvdGFsX2JlZF91c2FnZV9yYXRpbywgbmEucm0gPSBUUlVFKSkNCiAgDQogIGhycl9nZ3Bsb3RfZGF0YSA9IGhycl9zaGFwZV9kYXRhICU+JSANCiAgICBsZWZ0X2pvaW4oaHJyX2JlZF9yYXRpb3MsIGJ5ID0gYygiSFJSTlVNIiA9ICJocnJudW0iKSkgJT4lIA0KICAgIHNlbGVjdCghSFJSKSAgJT4lIA0KICAgIHN0X3RyYW5zZm9ybShjcnM9ICJFUFNHOjIxNjMiKQ0KICANCiAgaHJyX2dncGxvdF9kYXRhICU+JSANCiAgICBnZ3Bsb3QoKSArDQogICAgICBnZW9tX3NmKGFlcyhmaWxsID0gY292aWRfYmVkX3VzYWdlX3JhdGlvLzEwMCksIGxpbmV3aWR0aCA9IDAsIGNvbG9yPWFscGhhKCJibGFjayIsMC4wMikpICsNCiAgICAgIGdlb21fc2YoZGF0YSA9IHVzX3N0YXRlX3NoYXBlX2RhdGEsIGZpbGwgPSBhbHBoYSgiYmxhY2siLCAwLjApKSArDQogICAgICBzY2FsZV9maWxsX2NvbnRpbnVvdXMoIkJlZHMgVXNlZCBmb3IgQ292aWQiLCBsb3c9InB1cnBsZSIsIGhpZ2g9Im9yYW5nZSIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICAgICAgZ2d0aXRsZSgiSG9zcGl0YWwgQmVkIFVzYWdlIiwgc3VidGl0bGUgPSAiUGVyY2VudGFnZSBvZiBob3NwaXRhbCByZWZlcnJhbCByZWdpb24ncyBiZWRzIHVzZWQgYnkgY292aWQgcGF0aWVudHMiKSArDQogICAgICBteV9tYXBfdGhlbWUoKQ0KfQ0KDQpgYGANCg0KDQojIEV4YW1wbGUgR3JhcGhzDQoNCiMjIyBWYWNjaW5hdGlvbiBhbmQgSG9zcGl0YWxpemF0aW9uIFBvaW50IFBsb3QNCg0KYGBge3J9DQpkYXRlID0gIjIwMjEtMDktMjMiDQoNCnBsb3RfZGF0YSA9IEdlbmVyYXRlX1ZhY2NpbmF0aW9uX1Bsb3QoZGF0ZSwgInZhY2NfY29tcGxldGVfcGVyY2VudCIsICJjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8iKQ0KZ3JhcGhfaW50ZXJhY3RpdmUocGxvdF9kYXRhKQ0KDQpwbG90X2RhdGEgPSBHZW5lcmF0ZV9WYWNjaW5hdGlvbl9QbG90KGRhdGUsICJjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8iLCAidmFjY19jb21wbGV0ZV9wZXJjZW50IikNCmdyYXBoX2ludGVyYWN0aXZlKHBsb3RfZGF0YSkNCg0KcGxvdF9kYXRhID0gR2VuZXJhdGVfVmFjY2luYXRpb25fUGxvdChkYXRlLCAic2luZ2xlX2Rvc2VfcGVyY2VudCIsICJjb3ZpZF9iZWRfdXNhZ2VfdG90YWxfYmVkX3VzYWdlX3JhdGlvIikNCmdyYXBoX2ludGVyYWN0aXZlKHBsb3RfZGF0YSkNCmBgYA0KDQoNCiMjIyBWYWNjaW5hdGlvbiBSYXRlcyBNYXANCg0KIyMjIyBCeSBDb3VudHkNCg0KYGBge3J9DQpHcmFwaF9WYWNjaW5hdGlvbl9SYXRlc19CeV9Db3VudHkoIjIwMjEvMDMvMSIpDQoNCkdyYXBoX1ZhY2NpbmF0aW9uX1JhdGVzX0J5X0NvdW50eSgiMjAyMS8wOS8yNCIpDQoNCmBgYA0KDQojIyMjIEJ5IEhSUg0KDQpgYGB7cn0NCg0KZ3JhcGhfaW50ZXJhY3RpdmUoR3JhcGhfVmFjY2luYXRpb25fUmF0ZXNfQnlfSHJyKCIyMDIxLzA5LzI0IikpDQpgYGANCg0KDQoNCiMjIyBIb3NwaXRhbCBCZWQgVXNhZ2UgTWFwDQoNCg0KIyMjIyBCeSBDb3VudHkNCg0KYGBge3J9DQpHcmFwaF9Ib3NwaXRhbF9CZWRfVXNhZ2VfQnlfQ291bnR5KCIyMDIwLzExLzA2IikNCg0KR3JhcGhfSG9zcGl0YWxfQmVkX1VzYWdlX0J5X0NvdW50eSgiMjAyMi8wNy8yMiIpDQpgYGANCg0KIyMjIyBCeSBIUlINCg0KYGBge3J9DQpHcmFwaF9Ib3NwaXRhbF9CZWRfVXNhZ2VfQnlfSFJSKCIyMDIwLzExLzA2IikNCg0KR3JhcGhfSG9zcGl0YWxfQmVkX1VzYWdlX0J5X0hSUigiMjAyMi8wNy8yMiIpDQpgYGANCg0KIyBTZWxlY3RhYmxlIFN0YXQgTWFwDQoNCmBgYHtyfQ0KIyMjIFZhY2NpbmF0aW9uIFJhdGUgYnkgSEhSIEdyYXBoaW5nIEZ1bmN0aW9uDQoNCiMjIyBTdGF0IFNlbGVjdGFibGUgSW50ZXJhY3RpdmUgR3JhcGggb2YgVmFjY2luYXRpb24gUmF0ZXMgYnkgSFJSDQojIFZhbHVlcyBmb3IgYGRpc3BsYXlfc3RhdGA6DQojICAgICAgICAgdmFjY19jb21wbGV0ZV9wZXJjZW50OiBQZXJjZW50YWdlIG9mIHBlb3BsZSBmdWxseSB2YWNjaW5hdGVkIGluIHRoYXQgSFJSDQojICAgICAgICAgc2luZ2xlX2Rvc2VfcGVyY2VudDogICBQZXJjZW50YWdlIG9mIHBlb3BsZSB3aXRoIG9uZSB2YWNjaW5lIGRvc2UgaW4gdGhhdCBIUlINCiMgRGF0ZSBtdXN0IGJlIGdpdmVuIGluIHl5eXltbWRkIGZvcm1hdA0KR3JhcGhfVmFjY2luYXRpb25fUmF0ZXNfQnlfSHJyIDwtIGZ1bmN0aW9uKGRhdGUsIGRpc3BsYXlfc3RhdCkgew0KICANCiAgZ3JhcGhfc3RhdCA9IGVucXVvKGRpc3BsYXlfc3RhdCkNCiAgDQogIHZhY2NpbmF0aW9uX2RhdGEgPC0gY2FsY3VsYXRlX2hycl92YWNjaW5hdGlvbl9yYXRlcyhkYXRlKQ0KICANCiAgaHJyX2dncGxvdF9kYXRhID0gaHJyX3NoYXBlX2RhdGEgJT4lIA0KICAgIGxlZnRfam9pbih2YWNjaW5hdGlvbl9kYXRhLCBieSA9IGMoIkhSUk5VTSIgPSAiaHJybnVtIikpICU+JSANCiAgICBzZWxlY3QoIUhSUikgICU+JSANCiAgICBzdF90cmFuc2Zvcm0oY3JzPSAiRVBTRzoyMTYzIikgJT4lIA0KICAgIG11dGF0ZSh0ZXh0ID0gcGFzdGUwKA0KICAgICAgIkhSUiAjOiAiLCBIUlJOVU0sDQogICAgICAiPC9iPlxuU3RhdGU6ICIsIEhSUlNUQVRFX2xvbmcsIA0KICAgICAgIjwvYj5cbkZ1bGx5IFZhY2NpbmF0ZWQ6ICIsIGZvcm1hdCh2YWNjX2NvbXBsZXRlX3BlcmNlbnQsIGRpZ2l0cyA9IDQpLCAiJSIsDQogICAgICAiPC9iPlxuSGFkIFNpbmdsZSBEb3NlOiAiLCBmb3JtYXQoc2luZ2xlX2Rvc2VfcGVyY2VudCwgZGlnaXRzID0gNCksICIlIiwNCiAgICAgICI8L2I+XG5aaXAgQ29kZSBDb3VudDogIiwgaHJyX3BvcHVsYXRpb25femlwX3NsaWNlJHppcF9jb3VudFtIUlJOVU1dLA0KICAgICAgIjwvYj5cbkhSUiBQb3A6ICIsIGhycl9wb3B1bGF0aW9uX3ppcF9zbGljZSRwb3B1bGF0aW9uW0hSUk5VTV0pKQ0KICANCiAgaHJyX2dncGxvdF9kYXRhICU+JSANCiAgICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZigNCiAgICAgIGFlcyhmaWxsID0gISFncmFwaF9zdGF0LzEwMCArIHJ1bmlmKG5yb3coaHJyX2dncGxvdF9kYXRhKSwgbWluPTAsIG1heD0wLjAwMSksIA0KICAgICAgICAgIHRleHQ9dGV4dCksIA0KICAgICAgbGluZXdpZHRoID0gMC4xLCANCiAgICAgIGNvbG9yPWFscGhhKCJibGFjayIsMC41KSkgKw0KICAgIHNjYWxlX2ZpbGxfY29udGludW91cygNCiAgICAgICJIUlIgUG9wdWxhdGlvbiIsIA0KICAgICAgdHlwZSA9ICJ2aXJpZGlzIiwgDQogICAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIGJyZWFrcyA9IGMoMCwgLjIsIC40MCwgLjYwLCAuOCwgMSksICANCiAgICAgIGxpbWl0cz0gYyggMCwgMSkpICsNCiAgICBteV9tYXBfdGhlbWUoKQ0KfQ0KDQpncmFwaF9pbnRlcmFjdGl2ZShHcmFwaF9WYWNjaW5hdGlvbl9SYXRlc19CeV9IcnIoIjIwMjEvMDkvMjQiLCB2YWNjX2NvbXBsZXRlX3BlcmNlbnQpKQ0KYGBgDQogIA0KICANCi4uLg0KDQoNCg==